home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Belgian Amiga Club - ADF Collection
/
BS1 part 60.zip
/
BS1 part 60
/
LSD 31.adf
/
AmigaMachineLang1.pp
/
AmigaMachineLang1
Wrap
Text File
|
1990-09-07
|
154KB
|
2,926 lines
_____ _________________________________
/ /\ / \
/ / / / ___________________________ \
/ / / / / _________________________/\ \
/ / / / /_/___________ _____ \ \ \
/ / / / \ /\ \ \ \ \
/ / / /_________________ \ \ \ \ \ \ \
/ / / \________________/\ \ \ \ \ \ \ \
/ /_/__________________________\_\ \ \ \ \___\/ \
/ \ \ \ /
/____________________________________________\ \ \____________/
\____________________________________________/ \/___________/FISH
P R E S E N T
AMIGA MACHINE LANGUAGE - FROM ABACUS BOOKS
P A R T I
Typed by DEE JAY of X-CELL for LSD
Table of Contents.
------------------
1. Introduction
1.1 Why machine code?
1.2 A look into the Amiga's memory
1.2.1 RAM,ROM,hardware register
1.2.2 Bits,bytes and words
1.2.3 Number systems
1.3 Inside the Amiga
1.3.1 Components and libraries
1.3.2 Memory
1.3.3 Multi-tasking
2 The MC68000 processor
2.1 Registers
2.2 Addressing memory
2.3 Operating modes
2.3.1 User and supervisor modes
2.3.2 Exceptions
2.3.3 Interrupts
2.3.4 Condition codes
2.4 The 68000 Instructions
3 Working with assemblers
3.1 The development assembler
3.2 AssemPro
3.3 The K-SEKA assembler
4 Our first programs
4.1 Adding tables
4.2 Sorting tables
4.3 Converting number systems
4.3.1 Converting hex to ASCII
4.3.2 Converting decimal to ASCII
4.3.3 Converting ASCII to hex
4.3.4 Converting ASCII to decimal
5 Hardware registers
5.1 Checking for special keys
5.2 Timing
5.3 Reading the mouse or joystick
5.4 Tone production
5.5 Hardware registers overview
6 The Amiga operating system
6.1 Load libraries
6.2 Calling functions
6.3 Program initialization
6.3.1 Reserve memory
6.3.2 Opening a simple window
6.4 Input/output
6.4.1 Screen output
6.4.2 Keyboard input
6.4.3 Printer control
6.4.4 Serial I/O
6.4.5 Speech output
6.5 Disk operations
6.5.1 Open files
6.5.2 Reading and writing data
6.5.3 Erase files
6.5.4 Rename files
6.5.5 CLI directory
6.5.6 Read directory
6.5.7 Direct access to disk
7 Working with Intuition
7.1 Open screen
7.2 Openwindow
7.3 Requesters
7.4 Event handling
7.5 Menu programming
7.6 Text output
7.7 Images
7.8 Borders
7.9 Gadgets
7.9.1 Boolean gadgets
7.9.2 String gadgets
7.9.3 Proportional gadgets
7.10 Example program
8 Advanced programming
8.1 Supervisor mode
8.2 Exception programming
Appendix
Overview of library functions
Overview of the MC68000 Instruction
CHAPTER 1
---------
1.Introduction.
--------------
Before you tackle machine language,you should take a closer
look at several things that are vital to machine language
programming.
1.1.Why Machine Language.
------------------------
Machine language is actually the only language the MC68000
processor understands.All other languages,such as Basic,Pascal
or C,must first be translated(interpreted or compiled) into
machine code.This process can take place either when the
program is executed(the BASIC interpreter),or before program
execution(the Pascal and C compilers).
Advantages;
The great advantage of machine language over an interpreted
and compiled program is machine language programs are faster.
With an interpreter like BASIC,each line must first be
interpreted before it is executed,which requires a great deal
of time.A Pascal or C compiler translates the source into
machine language.This translation procedure does not produce
programs that are as fast as pure machine language programs.
Another advantage machine language has over BASIC is that an
interpreter is not needed for the execution of a machine
language program.
Machine language can access all the capabilities of the
computor since it is the language native to the computor.It
is possible that machine subroutines are required by a higher
level language to access functions that aren't directly
accessible by that language.
1.2.A Look Into The Amiga's Memory.
----------------------------------
Before a machine language program can be written,you must
know exactly what the program is required to do.You must also
be aware of what resources are needed and available to achieve
those goals.The most important of these resources is the
memory in the Amiga.
1.2.1.RAM,ROM,Hardware Register.
-------------------------------
Random Access Memory,referred to as RAM,allows information to
be placed in it and then withdrawn at a later time.This memory
consists of electronic componants that retain data only while
the computor is turned on(or until power failure).
So that the computor is able to do something when it is first
turned on,such as promting the Workbench or Kickstart disk,a
program has to remain in memory when the power is off.A memory
type which can retain data in memory without any power being
needed.This second memory type is known as ROM.
ROM;
ROM stands for Read Only Memory,indicating that data can only
be read from this memory,not written to it.The Amiga contains
a ROM,that loads the Workbench or Kickstart disk into RAM.The
first version of the Amiga did not contain the Kickstart in
ROM.
PROM;
One variation of ROM is the PROM,or Programmable Read Only
Memory.This special type of ROM can actually be programmed
once.Since it cannot be erased once programmed,it isn't
encountered very often.More often you will see EPROM's. or
Erasable Programmable ROM's.These special chips,which can be
erased with ultraviolet light,have a little window on the
surface of the chip usually covered with tape.
EEROM;
Although not available on the consumer market and much more
expensive than RAM,the EEROM(Electically Erasable ROM) offers
another alternative to programmable ROM.These chips function
like RAM,except that information is not lost when the power
is turned off.
WOM;
With the birth of the Amiga,another type of memory,WOM,was
created.This particular type of memory is Write Once Memory.
The Kickstart disk is read into this memory when the computor
is first booted.After this,no more data can be read into that
memory.Actually this isn't a completely new component,but
simply RAM that is locked once data has been read into it,
after which the data can only be read from that memory.
Registers;
In addition to RAM and these variations of ROM there is another
type of memory situated between those two groups.This memory
is connected to the processor through a group of peripheral
controllers.Thus it is commonly refered to as the Hardware
Register,since the computor's hardware is managed by this
system.We'll go into greater detail on how to use these hardware
registers later in this book.
Lets take a closer look at the structure and use of the memory
most familiar to us,RAM.
1.2.2.Bits,Bytes,and Words.
--------------------------
Kilobyte;
The standard size in which memory is measured is a Kilobyte
(Kbyte).One kilobyte consists of 1024 bytes,not 1000 as you
might expect.This unusual system stems from the computor's
binary mode of operation,where numbers are given as powers of
2,including kilobytes.
To access a memory block of one kilobyte,the processor requires
10 connections which carry either one volt or zero volts.Thus
2^10=1024 combinations or 1024 bytes of memory,are possible.
Byte;
A byte,in turn,consists of yes/no,on/off information as well.
A byte can be one of 2^8 different values,and thus it can
represent any one of 256 numbers.The individual numerical
values that make up a byte,which also are the smallest and
most basic unit encountered in any computor,are called bits
(short for binary coded digit).
A 512 kbyte memory,such as the Amiga's,contains 2^19=524288
bytes and 4194304 bits.It may seem unimaginable,but a memory
of that size has 2^4194300 different combinations.
Word;
Back to the basics...bits and bytes are sufficent to program
an eight bit processor like the 6500,since it can only work
with bytes.To program a 16/32 bit processor like the Amiga's
MC68000,you'll need to know two new data forms:words,consisting
of 16 bits(the equivalent of two bytes),and long words,which
are 32 bits(the equivalent of four bytes,or 2 words).
A word can be any number between 0 and 65536,a long word can
0 to 4294967295.The MC68000 processor can process these
gigantic numbers with a single operation.
Once in a while you need to use negative numbers as well as
positive ones.Since a bit can only be 1 or 0 and not -1,an
alternative system has been adopted.If a word is to have a
specific sign,the highest value digit or 15th bit in the word
(positions are always counted from zero) determines the sign
of the word.With this method words can carry values from -32768
to +32768.One byte can range from -127 to +127.In a byte,the
value -1 is given by $FF; in a word it's $FFFF,-2 is $FE(FFFE),
etc.
Lets stick with positive values for the time being,to aid in
the visualization of a bit in relation to its bit pattern.
Machine language does not use the familiar decimal system.
Instead,it commonly employs the binary as well as the octal and
hexadecimal number systems.
1.2.3.Number Systems.
--------------------
Lets take a look at the decimal system:its base number is 10.
This means that every digit represents a power of 10.This means
that the 246 represents 2*10^2+4*10^1+6*10^0.The decimal system
offers a selection of 10 characters,namely 0-9.
Binary;
This procedure is different for the binary system.The binary
system offers only two different characters:1 and 0.Thus the
systems base number is two.The decimal value of 1010 would be:
1*2^3+0*2^2+1*2^1+0*2^0=2^3+2^1=8+2=10 (in decimal system)
Generally binary numbers are identified by having a percentage
symbol as a prefix.See if you can determine the decimal value of
this number:110010...
Well did you get 50?.Thats the right answer.The most simple
method to arrive at this result is to simply add up the values
of the digits contained at 1.The values of the first eight digits
are as follows:
digit 8 7 6 5 4 3 2 1
value 128 64 32 16 8 4 2 1
Octal;
The octal system whose base is eight,is similar.The character set
consists of numbers 0 to 7.The decimal equivalent of the octal
number 31 is: 3*8^1+1*8^0=25.However the octal system isn't
nearly as important as the next one...
The base number of the hexadecimal system is 16,and its character
set ranges from 0 to F.Thus,A would be equivalent of a decimal 10
and F would be 15.The dollar sign($) indicates a hexadecimal
number.The binary and hexadecimal systems are the most important
numerical systems for assembly language programming.
Hex;
The hexadecimal representation of a byte ranging from 0 to 256
always has two digits:$00 to $FF.A word ranges from $0000 to $FFFF
and a longword from $00000000 to $FFFFFFFF.
Its quite easy to convert binary numbers into hexadecimal:simply
split up the binary numbers into groups of four digits.Each of
these groups of four digits then corresponds to one hexadecimal
digit.Heres an example:
binary number %110011101111
split up %1100 %1110 %1111
result $C $E $F
thus: %110011101111=$CEF
The opposite operation is just as easy...
hexadecimal $E30D
split up $E $3 $0 $D
result %1110 %0011 %0000 %1101
thus: $E30D = %1110001100001101
This method can also be used to convert binary into octal and vice
versa,except that groups of three digits are used in that case:
octal number 7531
split up 7 5 3 1
result %111 %100 %011 %001
thus: octal 7531=%111101011001
This binary number can the be converted into hexadecimal,as well:
binary number %111101011001
split up %1111 %0101 %1001
result $F $5 $9
thus: octal 7531=$F59
The following calculation can then be used to convert the number
into the familiar decimal system:
hexadecimal $F59
split up $F $5 $9
result 15*16^2+5*16+9
thus: $F59=3929 decimal
Although this conversions are quite simple ,they can get to be
rather annoying.Many assemblers can ease this task somewhat:they
allow you to enter a value with '?'upon which it returns the
value in decimal and hexadecimal forms.There are even calculators
that perform number base conversions.
Often this conversion as to be performed in a program,for instance
when a number is entered by the user and then processed by the
computor.In this case the number entered,being simply a
combination of graphic symbols,is evaluated and the usually
converted into a binary number,in effect,a word or a longword.
This process is often required in reverse order,as well.If the
computor is to display a calculated value in a specific number
system,it must first convert that number into a series of
characters.In a later chapter you will develop machine language
routines to solve these problems.You can then use these routines
in your own programs.First you still have to cover some things
that are fundamental to machine language programming on the Amiga.
1.3.Inside the Amiga.
--------------------
In order to program machine language,it is not sufficent to know
only the commands of the particular processor,one must also have
extensive knowledge of the Amiga being programmed.Lets take a
look inside the Amiga.
1.3.1.Components and Libraries.
------------------------------
The Amiga is a very capable machine,due to the fact that there
are components that do a large part of the workload,freeing up
the 68000 processor.These are refered to as the"custom"chips,
which perform various tasks independantly of the 68000 processor.
Custom Chips;
This task force is comprised of three chips,whose poetic names
are Agnus,Denice,and Paula.The main task of Agnus,alias blitter,
is the shifting of memory blocks,which is helpful for operations
such as quick screen changes.Denise is responsible for transfering
the computors thoughts on to the screen.Paula's tasks consist of
input/output jobs,such as disk operation or sound.
These chips are accessed by the processor through several adresses
starting at $DFF000,which are also known as the hardware registers
(you'll find more detailed information about the registers in the
corresponding chapter).To simplify the otherwise complicated
procedure of utilizing these chips,several programs have been
included in the Kickstart and Workbench libraries.These programs
can be called by simple routines and then take over the respective
chips.
If only these library functions are used to program the Amiga,the
parameters are the same,regardless of the language used.Only the
parameter notations differs from language to language.BASIC is an
exception in this respect,since its interpreter translates the
program calls,which is why you don't need to know how the Amiga
executes these functions in order to use them.
The library functions are written in machine language and are thus
closely related with your own machine language programs.Actually
you could do without the library programs and write all of the
functions yourself.However the incredible workload of this task is
so discouraging,that you'd rather stick with the library functions
1.3.2.Memory.
------------
First lets look at the RAM of the Amiga 1000.The standard version
of this computor has over 512 kbytes of RAM,ranging from the
address $00000 to $7FFFF,or 0 to 524287.If the memory is expanded
to one megabyte,the first address still starts at $00000,however
the start of anything greater than 512k can go anywhere in the
address space between $200000 to $9FFFFF.With the release of
AmigaDOS 1.2,the Amiga figures out where to put the memory
expansion by using a special`Autoconfig`scheme.This allows you to
add memory and I/O without worrying about addresses and dip
switches.
Chip RAM;
The chips that support the Amiga`s processor access RAM almost
totally independantly and thus ease the workload of the processor.
However there is a draw back:these chips can only access the first
512k bytes of RAM.Thus graphics and sound data handled by these
chips MUST be stored in this memory range.Because of this,that
memory range is referred to as `Chip RAM`.
Fast RAM;
The counterpart to chip RAM is the remaining RAM which,if the
computor is equipped with it,begins at $200000.Since only the
processor itself as access to this part of memory it is known has
`Fast RAM`.
Here`s an overview of the Amiga`s memory:
$000000-$07FFFF chip RAM
$080000-$1FFFFF reserved
$200000-$9FFFFF potential fast RAM
$A00000-$BEFFFF reserved
$BFD000-$BFDF00 PIA B (even addresses)
$BFE001-$BFEF00 PIA C (odd addresses)
$C00000-$DFEFFF reserved for expansion
$DFF000-$DFFFFF custom chip registers
$E00000-$E7FFFF reserved
$E80000-$EFFFFF expansion ports
$F00000-$F7FFFF reserved
$F80000-$FFFFFF system ROM
Since the Amiga is multi-tasking,when a program is loaded into
memory,it is simply loaded into another memory location.The memory
range thus occupied is added to a list of occupied memory and the
memory range is then considered barred from other uses.If another
program is loaded,which is quite possible with the Amiga,it is
read into another memory location which is then marked on the
occupied list.If the first program should require additional
memory,to use a text buffer for example,that memory first has to
be reserved.Otherwise another program could accidently be loaded
into the memory needed for this task.
What`s interesting about this procedure is that when the first
loaded has ended,the memory occupied by it is freed for further
use.As a result,RAM is then chopped up into occupied and free
parts,which are no longer related to each other.The Amiga can
still utilize these chunks of memory as if they were one
continuous chunk.After all,parts is parts.An example of this is
the dynamic RAM disk which is always available under the name RAM:
This RAM disk is actually quite a phenomenon,since it is always
completely filled.If a program is erased from RAM disk,the memory
allocated to that program,regardless of its location or structure,
is given back to the system.Thus,if you reserved and filled 100
kbytes of memory,it would be quite posible that the 100kbytes
actually consists of various pieces of memory independant of one
another.You never notice this since the Amiga automatically
corrects the difference between apparent and actual memory.
1.3.3.Multi-Tasking.
-------------------
The Amiga is truly an amazing machine,being capable of doing
several things at one time.A red and white ball might be bouncing
around in one window while you`re working on text in another
window and watching a clock tick away in a third.
At least that`s the impression most people get when they recieve
their first Amiga demonstration.However,there is a catch to this:
even the Amiga as only one processor,which can really only do one
thing at a time.
The tricky part is when more than one program is running,each
program is executed part by part,and the Amiga is constantly
switching from one program back to the other program.In the
example above,the ball would first be moved by one pixel,then
the processor would check for a text entry and if necessary display
it,after which it would move the clock`s second hand.This
procedure would be repeated over and over,as the three programs
are executed together.The problem is,that the greater the work
load on the processor,the slower the things happen.Thus,programs
run slower during heavy multi-tasking.
Tasks;
Each of these jobs that the Amiga has to execute are commonly
referred to has tasks...thus,multi-tasking.During multi-tasking,
each task is assigned a special time segment during which that
particular task is executed.These time segments can be controlled,
so that more time consumming programs can be allotted somewhat
more processing time.
The programmer actually doesn`t need to know how this time slicing
works.You can write aprogram without paying any attension to
multi-tasking and then run it simultaneously with another program
running in the background.The only restriction is that you`ll have
to start the program from the CLI with`run`,or from the Workbench.
If you execute the program from the CLI by simply typing its name,
the processor allots all the time it can get from the CLI to that
program,until the execution is complete.Starting the program with
run free`s the CLI for other uses while the program is being
executed.
There is another restriction regarding multi-tasking that applies
to assembler programmers.Aside from the use of extra memory,which
must first be reserved,the hardware registers should not be
directly accessed.Instead the library functions should be used.The
reason for this is quite simple:
Should you,for instance,specify the printer port as the input line
and are reading data in,another task might suddenly think its
supposed to be printing.The line would thus be switched to output
and data would be written out.After this,your program would try to
read more data in,which would not be possible.
This is an oversimplified example,but it points out the problem
nevertheless.In real programming situations the effects of
multiple direct programming of the hardware registers can be much
more catastrophic.If your program still needs to access the
hardware registers directly(which can have some advantages),then
make sure that the program always runs by itself.
Chapter 2.
---------
2.The MC68000 Processor.
-----------------------
The Amiga`s MC68000 processor is a 16/32 bit processor,which means
that while it can process data of 32 bits,it"only"has a 16 bit
data bus and a 24 bit address bus.Thus,it can access 2^24=16777216
bytes(or 16 Mbytes)of memory directly.
7.1 Megaherz;
The Amiga 68000 processor,running at 7.1 megaherz,is quite fast,
which is required for a computor with a workload as heavy as the
Amiga`s.The Amiga also processes a number of custom chips that
greatly ease the workload of the processor.These custom chips
manage sound in/output,graphics and animation,thus freeing the
processor for calculations.
2.1.Registers.
-------------
In addition to the standard RAM,the processor contains internal
memory called registers.There are eight data registers(D0-D7),
eight address registers(A0-A7),a status register(SR),two stack
pointers,a user stack pointer,a system stack pointer(USP and SSP)
and the program counter(PC).
Register Sizes;
The data registers,the address registers,and the program counter
are all 32 bits,while the status register is 16 bits.These
registers are located dirctly in the processor so they are not
accessed the same way memory would be accessed.There are special
instructions for accessing these registers.
Data Registers;
The data registers are used for all kinds of data.They can handle
operations with bytes(8 bits)words(16 bits)and longwords(32bits).
Address Registers;
The address registers are used for storing and processing
addresses.This way they can be used as pointers to tables,in which
case only words and longwords operations are possible.
Stack Pointer;
The address register A7 plays a special role:this register is
utilized as the Stack Pointer(SR)by the processor,and thus is not
recommended for normal use by the programmer.Which of the two
possible stacks is being pointed to depends on the present mode of
the processor,but more about that later.
The stack,to whose actual position the stack pointer is pointing,
is used to store temporary internal data.The stack works similar
to a stack of notes on your desk:the note that was added to the
stack last is the first one to come off the stack.This type of
stack is known as LIFO(Last in,First out).There is another type of
stack,the FIFO(First in,First out)which is not used by the
processor itself.
How these registers and the SP can be manipulated,or how to work
with the stack,is presented in the next chapter.Lets continue with
the registers for now.
Status Register;
The status register plays an important role in machine language
programming.This 16-bit quality(word)contains important
information about the processor status in 10 of its bits.The word
is divided into two bytes,the lower byte(the user byte)and the
upper byte(the system byte).The bits that signify that certain
conditions are refered to as flags.This means that when a certain
condition is present,a particular bit is set.
The user byte contains five flags,which have the following meaning
Bit Name Meaning
-----------------------------------------------
0 (C,Carry) Carry bit,modified by math
calculation,and shift instructions.
1 (V,Overflow) Similar to carry,indicates a change
of sign,in other words,a carry from
bit six to bit seven.
2 (Z,Zero) Bit is set when the result of an
operation is zero.
3 (N,Negative) Is set when the result of an
operation is negative.
4 (X,Extended) Like carry,is set for arithmetic
operations.
5-7 Not used.
The system byte contains five significant bits:
Bit Nane Meaning
-----------------------------------------------
8 I0 Interupt mask.Activates interupt
9 I1 levels 0 to 7,where 0 is the lowest
10 I2 and 7 is the highest priority.
11 not used.
12 not used.
13 (S,Supervisor) This bit indicates the actual
pocessor mode(0=User,1=Supervisor
mode).
14 not used.
15 (T,Trace) If this bit is set,the processor is
in single step mode.
Here's an overview of the status word;
bit : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
name : T - S - - I2 I1 I0 - - - X N Z V C
Don't let the new terms,like mode and interupt confuse you.We'll
talk about these in greater detail in the chapter dealing with the
operating conditions of the processor.
2.2.Addressing Memory.
---------------------
In the standard Amiga 500's and 1000's,the processor has over 512k
of RAM available.The Amiga 2000 has one mega-byte of RAM that can
be accessed by the processor.How does the processor access all
this memory?
If you're programming in BASIC you don't have to worry about
memory management.You can simply enter MARKER%=1,and the value is
stored in memory by the BASIC interpreter.
In assembler,there are two ways of accomplishing this task:
1) Store the value in one of the data or address registers,or
2)Write it directly into a memory location.
To demonstrate these two methods let's get a little ahead and
introduce a machine language instruction,which is probably the
most common:MOVE.As its name states,this instruction moves values.
Two parameters are required with the instruction:source and
destination.
Lets see what the example from above would look like if you
utilize the MOVE instruction...
1) MOVE #1,D0
This instruction moves the value 1 into data register D0.As you
can see,the source value is always entered before the destination.
Thus,the instruction MOVE D0,#1 is not possible.
2) MOVE #1,$1000
deposits the value 1 in the memory location at $1000.This address
was arbitrarily chosen.Usually addresses of this form won't be
used at all in assembler programs,since labels that point to a
certain address are used instead.Thus,the more common way of
writing this would be:
...
MOVE #1,MARKER
...
MARKER:DC.W 1
These are actually two pieces of program:the first part executes
the normal MOVE instruction whose destination is `MARKER`.This
label is usually defined at the end of a program and specifies the
address at which the value is stored.
The paraneter DC.W 1 is a pseudo op,apseudo operation.This means
that this isn`t an instruction for the processor,but an
instruction for the assembler.The letters DC stand for`DeClare`and
the suffix .W indicates that the data is a Word.The other two
suffix alternatives would be .B for a byte(8 bits)and .L for a
long word(32 bits).
This suffix(.B.W or.L)is used with most machine language
instructions.If the suffix is omitted,the assembler uses .W(word)
as the default parameter.If you wanted a long word,you`d use an
instruction that looks something like this:MOVE .L #$1234678,D0
where as an instruction like MOVE.B #$12,D0 would be used for a
byte of data.However,with this instruction there`s one thing you
must be aware of...
CAUTION:
If the memory is accessed by words or long words,the address must
be even(end digit must be 0,2,4,6,8,A,C,E)!
Assemblers usually have a pseudo-op,`EVEN`or`ALIGN`,depending on
the assembler,that aligns data to an even address.This becomes
necessary in situations similar to this:
...
VALUE1: DC.B 1
VALUE2: DC.W 1
If the VALUE1 is located at an even address,VALUE2 is automaticaly
located at an odd one.If an ALIGN(EVEN)is inserted here,a fill
byte(0)is inserted by the assembler,thus making the second address
even.
...
VALUE1: DC.B 1
ALIGN
VALUE2: DC.W 1
Back to the different ways of addressing.The variations listed
above are equivalent to the BASIC instruction MARKER%=1 where the
% symbol indicates an integer value.
Lets go a step further and translate the BASIC instruction MARKER
%=VALUE% into assembler.You`ve probably already guessed the answer
right?
MOVE VALUE,MARKER
...
...
MARKER: DC.W 1
VALUE : DC.W 1
In this case,the contents in the address at VALUE are moved into
the address at MARKER.
With the help of these simple examples,you`ve already become
familiar with four different ways of addressing,in other words,
ways that the processor can access memory.The first is
characterized by the number sign(#)and represents a direct value.
Thus,this method is also known as direct addressing,and is legal
only for the source parameter!
A further method in which a direct address(in our case,`MARKER`and
`VALUE`)can be specified is known as absolute addressing.This
method is legal for the source parameter as well as for the
destination parameter.
This method can be divided into two different types,between which
the programmer usually does'nt notice a difference.Depending on
whether the absolute address is smaller or larger than $FFFF,in
other words if it requires a long word,it is called absolute
addressing(for addresses above $FFFF)or otherwise absolute short
addressing.The assembler generally makes the distinction between
these two types,and thus,only general knowledge of absolute
addressing is required.
The fourth method of addressing that you've encountered so far is
known as DATA REGISTER DIRECT.It was the first one introduced(MOVE
#1,D0)in conjunction with direct addressing,the only difference
being that this type accesses a data register(such as D0).
These four methods aren't the only ones available to the MC68000
processor,in fact there are a total of 12.One other variation
called ADDRESS REGISTER DIRECT,is almost identical to data
register direct,except that it accesses the address register
instead of the data register.Thus,you can use MOVE.L #MARKER,A0 to
access the address register A0 directly.
You now know five ways to address memory with which quite a bit
can be accomplished.Now,lets tackle something more complicated and
more interesting.
Lets take another example from BASIC:
10 A=1000
20 POKE A,1
In this example the first line assigns the value 1000 to the
variable A.This can be done in assembler as well:MOVE.L #1000,A0.
In the assembler version the absolute value of 1000 ia stored in
the address register A0.
Line 20 doesn't assign a value to the variable A itself,but rather
to the memory location at the address stored in A.This is an
indirect access,which is quite easy to duplicate in assembler:
MOVE.L #1000,A0 ;bring address in A0
MOVE #1,(A0) ;write 1 into this address
The parentheses indicates an addressing known as ADDRESS REGISTER
INDIRECT.This method only works with address registers,since a
'data register indirect' does not exist.
There are several variations of this method.For instance,a
distance value can be specified,which is added to the address
presently located in the address register before the register is
actually accessed.The instruction MOVE #1,4(A0),if applied to the
example above,writes the value 1 into the memory cell at 1000+4=
1004.This distance value can be positive or negative.Thus,values
from -32768 to +32768 are accepted.This specific variation of
addressing is called ADDRESS REGISTER INDIRECT WITH A 16 BIT
DISPLACEMENT value.
There is another very similar variation:ADDRESS REGISTER INDIRECT
WITH AN 8 BIT INDEX value.While this variation is limited to 8
bits,it also brings another register into play.This second
register is also a distance value,except that it is a variable as
well.
We'll try to clarify this with an example.Lets assume that a
program includes a data list that is structured like this:
...
RECORD: DC.W 2 ;number of entries -1
DC.W 1,2,3 ;elements of list
We'll use MOVE.L #RECORD,A0 to load the list into the address
register A0.Then you can use MOVE (A0),D0 to pull the number of
in the list into the data register.To access the last element of
the listonly one instruction is needed.The whole thing looks
something like this:
CLR.L D0 ;erase D0 completely
MOVE.L #RECORD,A0 ;address of list in A0
MOVE (A0),D0 ;number of elements -1 in D0
MOVE 1(A0,D0),D1 ;last element in D1
...
RECORD: DC.W 2 ;number of entries -1
DC.W 1,2,3 ;elements of list
This last instruction accesses the byte that is located at 1+A0+D0
in other words,the record +1 where the data begins plus the
contents of D0(in this case 2).
This method of accessing is very useful.It works exquisitely for
the processing of tables and lists,as the example demonstrates.If
no distance value is needed,simply use a distance value of zero,
which some assemblers automatically insert as the default value if
for instance only MOVE (A0,D0)is entered.
The latter two methods have a third variation,which as its own
characteristic trait.It dosen't utilize an address register,but
uses the Program Counter(PC)instead.The program counter with
displacement method proves useful when a program must function
without any changes in all address ranges.The following two
statements(in the 15 bit limits)have the same effect:
MOVE MARKER,D0
and
MOVE MARKER(PC),D0
This method is actually rather imprecise,since the first
instruction specifies the actual address of the marker with MARKER
while the second line specifies the distance between the
instruction and the marker.However since it would be quite
cumbersome to constanly calculate the distance,the assembler takes
this task off our hands and calculates the actual value automatic.
Lets examine the difference between the two instructions.In a
program they'll accomplish the same thing,although they are
interpreted as two completely different things by the assembler.
You'll assume a program being at the address $1000 and the marker
is located at $1100.The generated program code looks something
like this:
$001000 30 39 00 00 11 00 MOVE MARKER,D1
or
$001000 30 3A 00 FE MOVE MARKER(PC),D1
As you can see,the generated code of the second line is two bytes
shorter than the first line.In addition,if you were to shift this
code to the address $2000,the first version still accesses the
memory at $1100,while the second line using the PC indirect
addressing accesses memory at $2000 correctly.Thus,the program can
be transferred to almost any location.
This,then,is PROGRAM COUNTER WITH 16 BIT DISPACEMENT value.As we
mentioned,there is also PROGRAM COUNTER WITH 8 BIT INDEX value,
which permits a second register as a distance value,also known as
offset.
There are two addressing modes left.These two are based on
indirect addressing.They offer the capability of automatically
raising or lowering the address register by one when memory is
accessed with address register indirect.
To automatically increase the register,you'd use ADDRESS REGISTER
DIRECT WITH POST-INCREMENT.The address register raises this by the
number of bytes used AFTER accessing memory.Thus if you write:
MOVE.L #1000,A0
MOVE.B #1,(A0)+
the 1 is written in the address $1000 and then A0 is raised by one
Instructions like this are very helpful when a memory range is to
be filled with a specific value(for instance when the screen is
cleared).For such purposes the instruction can be placed in a loop
...which we'll get to later.
The counter part to post increment is ADDRESS REGISTER WITH PRE-
DECREMENT.In this case the specified address register is lowered
by one BEFORE the access to memory.The instructions:
MOVE.L #1000,A0
MOVE.B #1,-(A0)
writes 1 in the address 999,since the contents of A0 is first
decremented and the 1 is written afterwards.
These two methods of addressing are used to manage the stack
pointer(SP).Since the stack is filled from top to bottom,the
following is written to place a word(s.aD0)on the stack:
MOVE.B D0,-(SP)
and to remove it from the stack,again in D0:
MOVE.B (SP)+,D0
This way the stack pointer always points to the byte last
deposited on the stack.Here,again,you'll have to be careful that
an access to the stack with a word or a long word is always on an
even address.Thus,if you're going to deposit a byte on the stack,
either use a whole word or make sure the byte is not followed by a
JSR or BSR.The JSR or BSR instructions deposit the addresses from
which they stem on the stack in the form of a long word.
In the code above,the SP is generally replaced by the address
register A7 in the program code,since this register is always used
as the SP.Thus,you can write A7 as well as S,the resulting program
is the same.However,we recommend the use of SP,since this makes
the code somewhat easier to read.After all,quite often you'll want
to employ your own stacks,in which case the difference between the
processor stack and your own stacks become very important.
These are the twelve ways of addressing the MC68000 processor,Here
is a summary:
No Name Format
-- ---- ------
1 data register direct Dn
2 address register direct An
3 address register indirect (An)
4 address register indirect with post-increment (An)+
5 address register indirect with pre-decrement -(An)
6 address register indirect with 16 bit displacement d16(An)
7 address register indirect with 8 bit index value 8(An,Rn)
8 absolute short xxxx.W
9 absolute long xxxxxxxx.L
10 direct #'data'
11 program counter indirect with 16 bit displacement d16(PC)
12 program counter indirect with 8 bit index value d8(PC,Rn)
The abbreviations above have the following meanings:
An address registers A0-A7
Dn data registers D0-D7
d16 16 bit value
d8 8 bit value
Rn register D0-D7,A0-A7
'data' up to 32 bit value,(either .B .W .L)
These are the addressing modes used by the MC68000 processor.The
bigger brother of this processor,the 32 bit MC68020,has six more
methods which we won't discuss here.
Next you're going to see under what conditions the processor can
operate.
2.3.Operating Modes.
-------------------
In the previous section about registers you encountered the Status
Register(SR).The individual bits of this register reflect the
present operating condition of the processor.You differentiated
between the system byte(bits 8-15)and the user byte(bits 0-7).Now,
lets take a closer look at the system byte and its effects upon
the operation of the processor.
2.3.1.User and Supervisor Modes.
-------------------------------
Isn't it rather strange that the processor classifies you either
as a'user'or a 'supervisor'?Both of these operating modes are
possible,the user mode being the more common mode.In this mode it
is impossible to issue some instructions,and that in your own
computor!
Don't worry though,you can get around that,as well.The Amiga's
operating system contains a function that allows us to switch the
processor from one mode to the other.
The mode is determined by bit 13 of the Status Register.Usually
this bit is cleared(0),indicating that the processor is in user
mode.It is possible to write directly into the status register,
although this is a privileged instruction that can only be
executed from the supervisor mode.Thus,this instructioncould only
be used to switch from the supervisor mode into the user mode,by
using AND #$DFFF,SR to clear the supervisor bit.However,it is
quite preferable to let the operating system perform the switch
between these two modes.
Now what differentiates these two modes in terms of application?
Well,we already mentioned the first difference:some instructions,
such as MOVE xx,SR, are privileged and can only be executed from
the supervisor mode.An attempt to do this in user mode would
result in an exception and interruption of the program.Exceptions
are the only way of switching to the supervisor mode,but more
about later.
A further difference is in the stack range used.Although A7 is
still used as the stack pointer,another memory range is used for
the stack itself.Thus,the SP is changed rach time you switch from
one mode to the other.Because of this you differentiate between
the User(USP)and the Supervisor SP(SSP).
Accessing memory can also depend on these two modes.During such
accessing,the processor sends signals to the peripheral components
informing them of the current processor moce.This way a MC68000
computor can protect(privilege)certain memory ranges so they can't
be accessed by the user.
In the supervisor mode it is possible to execute all instructions
and access all areas of memory.Because of this,operating systems
usually run in the supervisor mode.This is accomplished through
the use of Exceptions.
2.3.2.Exceptions.
----------------
Exceptions are similar to interupts on 6500 computors.This allows
stopping a program,running a sub-program,and then restarting the
stopped program.When an exception occurs the following seps are
taken:
1) The status register is saved.
2) The S bit in SR is set(supervisor mode)and the T bit is
cleared(no trace).
3) The program counter and the user SP are saved.
4) The exception vector,which points to the needed exception
routine,is retrieved.
5) The routine is executed.
The vectors mentioned,which contain the starting addresses for the
various routines,are located at the very beginning of the memory.
Here is an overview of the vectors and their respective addresses:
Number Address Used for
----- ------ --------
0 $000 RESET:starting SSP
1 $004 RESET:starting PC
2 $008 bus error
3 $00C address error
4 $010 illegal instruction
5 $014 division by zero
6 $018 CHK instruction
7 $01C TRAPV instruction
8 $020 privilege violation
9 $024 trace
10 $028 Axxx-instruction emulation
11 $02C Fxxx-instruction emulation
$030-$038 reserved
15 $03C uninitialed interrupt
$040-$05F reserved
24 $060 unjustified interrupt
25-31 $064-$083 level 1-7 interrupt
32-47 $080-$0BF TRAP instructions
$0C0-$0FF reserved
64-255 $100-$3FF user interrupt vectors
The individual entries in the table above need detailed
explanation.So lets go through them one by one...
RESET:staring SSP;
At reset,the long word stored at this location is used as the
stack pointer for the supervisor mode(SSP).This way you can
specify the stack for the RESET routine.
RESET:starting PC;
Again at reset,the value at this location is used as the program
counter.In other words,the RESET routine is started at the address
stored here.
Bus Error;
This exception is activated by a co-processor when,for instance,a
reserved or non-existant memory range is accessed.
Address Error;
This error occurs when a word or long word access is atempted at
an odd address.
Illegal Instruction;
Since all MC68000 instructions consist of one word,a total 65536
different instructions are possible.However,since the processor
doesn't that many instructions,there are a number of words that
are invalid instructions.Should such a word occur,the exception is
prompted.
Division by Zero;
Since the processor has a division function,and the division of
anything by zero is mathematically undefined and thus illegal,this
exception occurs when such an operation is attempted.
CHK Instruction;
This exception only occurs with the CHK instruction.This
instruction tests that a data registers contents are within a
certain limit.If this is not the case,the exception is activated.
TRAPV Instruction;
If the TRAPV instruction is executed and the V bit(bit 1)in the
status word is set,this exception is prompted.
Privilege Violation;
If a privileged instruction is called from the user mode,this
exception is activated.
Trace;
If the trace bit(bit 15)in the status word is set,this exception
is activated after each instruction that is executed.This method
allows you to employ a step by step execution of machine programs.
Axxx-Instruction Emulation;
Fxxx-Instruction Emulation;
These two vectors can be used for a quite interesting trick.If an
instruction beginning with $A or $F(such as $A010 or $F200)is
called the,the routine to which the corresponding vector is
pointing is accessed.In these routines you can create chains of
other instructions,in effect expanding the processors instruction
vocaulary!
Reserved;
These vectors are not used.
Uninitialized Interrupt;
This exception is activated when a peripheral component that was
not initialized sends an interrupt.
Unassigned Interrupt;
Is activated when a BUS error occurs during the interrupt
verification of the activating component.However,the interrupt is
usually only by some type of disturbance.
Level 1-7 Interrupt;
These seven vectors point to the interrupt routines of the
corresponding priority levels.If the level indicated in the status
word is higher than the level of the occuring interrupt,the
interrupt is simply ignored.
TRAP Instructions;
These 16 vectors are used when a corresponding TRAP instruction
occurs.Thus,TRAP instructions from TRAP #0 to TRAP #15 are
possible.
User Interrupt Vectors;
These vectors are used for interrupts which are activated by
several peripheral components that generate their own vector
number.
At this point you don't want to delve any deeper into the secrets
of exceptions,since we'd be expanding this book beyond its frame
work.However,there's one last thing to say about exceptions:the
exception routines are ended with the RTE(ReTurn from Exception)
instruction,with which the original status is restored and the
user program is continued.
2.3.3.Interrupts.
----------------
Interrupts are processed similarly to exceptions.They are breaks
(or interruptions)in the program which are activated through
hardware(such as a peripherical component or an external trigger).
The interrupt level is stored in bits 8-10 of the status register.
A value between 0 and 7 indicates the interrupt level.There are
eight possible interrupts,each of which as a different priority.If
the level of this interrupt happens to be higher than the value in
the status register,the interrupt is executed,or else ignored.
When a valid interrupt occurs,the computor branches to the
corresponding routine whose address is indicated in the exception
vector table above.
The interrupts are very important if you're trying to synchronize
a program with connected hardware.In this manner,a trigger(s.a the
keyboard)which is to feed the computor data,can signal the request
for a legal value using an interrupt.The interrupt routine then
simply takes the value directly.This method is also employed for
the operation of the serial interface(RS232).
We'll talk about the use of interrupts at a later time.The last
thing we want to mention about interrupts at this time is that,
like exceptions,interrupt routines are terminated with the RTE
instruction.
2.3.4.Condition Codes.
---------------------
When you write a program in any language,the need for a
conditional operation arises quite often.For instance,in a BASIC
program
IF D1=2 THEN D2=0
represents a conditional operation.To write the equivalent in
machine language,you first need to make the comparison:
CMP #2,D1
CMP stands for compare and compares two operands,in this case D1
and D2.How is this operation evaluated?
For this purpose you have condition codes(CC's),which exist in the
branch instructions of machine language.Because of this,you must
specify when and where the program is to branch.
The simplest variation of the branch instruction is an
unconditional branch.The corresponding instruction is 'BRA address
',although this won't help you here.After all,you need a
conditional branch.
To retain the result of the operation,in this case a comparrison
(CMP),several bits are reserved in the status word.Lets look at
bit 2 first,which is the zero flag.This flag is set when the
result of the previous operation was zero.
To explain the relationship between CMP and the Z flag,you must
first clarify the function of the CMP instruction.Actually this
instruction performs the subtraction of the source operand from
the destination operand.In the example above,the number 2 is
subtracted from the content of the memory cell at D1.Before the
result of this subtraction is discarded,the corresponding flags
are set.
If the contents of D1 in our example above happened to be 2,the
result of the subtraction would be 0.Thus,the Z flag would be set,
which can then be evaluated through a corresponding branch
instruction.Our example would then look like this:
...
CMP #2,D1 ;comparison,or subtraction
BNE UNEQUAL ;branch,if not equal(Z flag not set)
MOVE #0,D2 ;otherwise execute D2=0
UNEQUAL:
... ;program branches to here
BNE stands for Branch if Not Equal.This means,that if the Z flag
was cleared(=0)by the previous CMP,the program branches to the
address specified by BNE(here represented by UNEQUAL).The counter
part to the BNE instruction is the BEQ(Branch if EQual)instruction
which is executed if Z=1.
Here's a list of condition codes,which allow you to form
conditional branches using the Bcc(cc=condition code)format:
cc Condition Bits
---------------------------------------------------
T true,corresponds to BRA
F false,never branches
HI higher than C'* Z'
LS lower or same C + Z
CC,HS carry clear,higher or same C'
CS,LO carry set,lower C
NE not equal Z'
EQ equal Z
VC overflow clear V'
VS overflow set V
PL plus,positive
MI minus,negative
GE greater or equal N*V+N'*V'
LT less than N*V'+N'*V
GT greater than N*V*Z'+N'*V'*Z'
LE less or equal Z + N*V' + N'*V
*=logic AND, +=logic OR, '=logic NOT
Here are a few examples to demonstrate how these numerous
conditions can be utilized:
CMP #2,D1
BLS SMALLER_EQUAL
This branches if the contents of D1<=2,whether D1 is 0,1 or 2.In
this example,the BLE instruction would allow the program to branch
even if D1 is negative.You can tell this by the fact that the V
bit is used in the evaluation of this expression(see chart above).
When the sign is changed during the operation,this V bit is
compared with the N bit.Should both bits be cleared(N bit=0 and V
bit=0)after the CMP subtraction(D1-2),the result has remained
positive:the condition as not been met.
The conditions EQ and NE are quite important for other uses,as
well.For instance,they can be used to determine if particular bits
in a data word are set,by writing the following sequence...
...
AND #%00001111,D1 ;masks bits out
BEQ SMALLER ;branches when none of the four
; ;lower bits is set
CMP #%00001111,D1
BEQ ALL ;branches when all four bits set
The AND instruction causes all bits of D1 to be compared with the
bits of the parameter(in this case #%00001111).If the same bits
are set in both bytes,the corresponding bits are also set in the
result.If one bit of the pair is cleared,the resulting bit is zero
as well.Thus,in the result,the only bits that are set are those
bits of the lowest four that were set in D1.
The technique is known as masking.In the example above,only the
lowest four bits were masked out,which meansthat in the resulting
byte,only the lowest four appear in their original condition.All
other bits are cleared with the AND operand.Of course you can use
any bit combination with this method.
If no bit at all is set in the result,the zeroflag is set,thus
fullfilling the BEQ condition and branching the program.Otherwise,
the next instruction is processed,in which D1 is compared with
%00001111.When both are equal,at leastall of the four lowest bits
of the original byte have been set,in which case the following BEQ
instruction branches.
Aside from CMP,the CC and CS conditions can also be used to
determine whether a HI bit was pushed out of the data word during
data rotation with the ROL and ROR instructions.
Before you move on the instruction vocabulary of the MC68000,we'd
like to give you another tip:
The AssemPro assembler makes it quite easy to try every command in
posible situations.Take the CMP command which we've been talking
about,for example.To test this command with various values and to
receive the results of the comparisons directly via the flags,try
the following.
Type the following into the Editor.
run:
cmp $10,d1
bra run
end
Assemble it,save the resulting code and enter the debugger.After
reloading the code you can then single step through the program
observing the results the program has on the flags.Try changing
the values in the register D1 and see how higher and lower values
affect the flag.
By the way,using the start command at this time causes it to run
forever.Well,at least until reset is hit,which isn't exactly
desirable,either...
This procedure isn't limited to just the CMP instruction.You can
use it to try any other instructions you're interested in.
2.4.The 68000 Instructions.
--------------------------
Its about time to explain the MC68000 instructions.You don't have
room for an in-depth discussion of each instruction in this book;
for that purpose we recommend PROGRAMMING THE 68000 from Sybex by
Steve Williams.
The following tables show the required parameters and arguments
for each instruction.AssemPro have access to built in help tables
covering effective addressing modes and many of the Amiga
Intuition calls.The following notation is used for arguments:
Label a label or address
Reg register
An address register n
Dn data register n
Source source operand
Dest destination operand
<ea> address or register
#n direct value
Here is a short list of the instructions for the MC68000 processor
AssemPro owners can simply place the cursor at the beginning of
the instruction and press the help key to see the addressing modes
allowed:
Mnemonic Meaning
---------------------------------------------------
Bcc Label conditional branch,depends on condition
BRA Label unconditional branch(similar to JMP)
BSR Label branch to subprogram.Return address is
deposited on stack,RTS causes return to that
address.
CHK <ea>,Dx check data register for limits,activate the
CHK instruction exception.
DBcc Reg,Label check condition,decrement and branch
JMP Label jump to address(similar to BRA)
JSR Label jump to subroutine.Return address is
deposited on stack,RTS causes return to that
address.
NOP no operation
RESET reset peripherals(caution!)
RTE return from exception
RTR return with loading of flags
RTS return from subroutine(after BSR and JSR)
Scc <ea> set a byte to -1 when condition is met
STOP stop processing(caution!)
TRAP #n jump to an exception
TRAPV check overflow flag,the TRAPV exception
Here are a few important notes...
When a program jumps(JSR)or branches(BSR)to subroutine,the return
address to which the program is to return is placed on the stack.
At the RTS instruction,the address is pulled back off the stack,
and the program jumps to that point.
Lets experiment a little with this procedure.Please enter the
following short program:
run:
pea subprogram ;address on the stack
jsr subprogram ;subprogram called
move.l (sp)+,d1 ;get a long word from stack
; illegal ;for assemblers without
;debuggers
subprogram:
move.l (sp),d0 ;return address in D0
rts ;and return
end
The first instruction,PEA,places the address of the subprogram on
the stack.Next,the JSR instruction jumps to the subprogram.The
return address,or the address at which the main program is to
continue after the completion of the subprogram,is also deposited
on the stack at this point.
In the subprogram,the long word pointed to by the stack pointer is
now loaded into the data register D0.After that,the RTS
instruction pulls the return address from the stack,and the
program jumps to that address.
Back in the main program,the long word which is on the top of the
stack,is pulled from the stack and written in D1.Assemblers that
do not have the debugging features of AssemPro may need the
ILLEGAL instruction so they can break the program and allow you to
view the register contents.
Assemble the program and load the resulting code into the debugger
Single step thru the program and examine the register contents.
Here you can see that D0 contains the address at which the program
is to continue after the RTS command.Also,D1 contains the address
of the subprogram which you can verify by comparing the debugger
listing.
The STOP and RESET instructions are so powerful that they can only
be used in supervisor mode.Even if you do switch to the supervisor
mode,you should NOT use these instructions if there is any data in
memory that has not been saved and you wish to retain.
The TRAP instruction receives a number between 0 and $F,which
determines the particular TRAP vector(addresses $0080-$00BF)and
thus the corresponding exception routine.Several operating systems
for the 68000 utilize this instruction to call operating system
functions.You'll deal more with this instruction later.
In the short sample program that compared two numbers,the CMP
instruction performed an arithmetic function,namely a subtraction.
This subtraction could be performed with an actual result as well
using the SUB instruction.The counterpart to this is in addition,
for which the ADD instruction is used.In 8 bit processors,like the
6502,these arithmetic functions are the only mathematical
operations.The MC68000 can also multiply,divide,and perform these
operations with a variety of data sizes.
Most of the functions require two parameters.For instance the ADD
instruction...
ADD source,destination
where source and destination can be registers or memory addresses.
Source can also be a direct value(#n).The result of the opoeration
is placed in the destination register or the destination address.
This is same for all operation of this type.These instructions can
be tried out with the AssemPro assembler.In this case we recommend
the use of a register as the destination.
Heres an overview of the arithmetic operations with whole numbers:
Mnemonic Meaning
--------------------------------------------------------------
ADD source,dest binary addition
ADDA source,An binary addition to a address register
ADDI #n,<ea> addition with a constant
ADDQ #n,<ea> fast addition of a constant which can
be only from 1-8
ADDX source,dest addition with transfer in X flag
CLR <ea> clear an operand
CMP source,dest comparison of two operands
CMPA <ea>,An comparison with an address register
CMPI #n,<ea> comparison with a constant
CMPM source,dest comparison of two memory operands
DIVS source,dest sign-true division of a 32 bit
destination by a 16 bit source operand.
The result of the division is stored in
the LO word of the destination,the
remainder in the HI word.
DIVU source,dest division without regard to sign,similar
to DIVS
EXT Dn sign-true expansion to twice original
size(width)data unit
MULS source,dest sign-true multiplication of two words
into one long word
MULU source,dest multiplication without regard to sign,
similar to MULS
NEG <ea> negation of an operand(twos complement)
NEGX <ea> negation of an operand with transfer
SUB source,dest binary subtraction
SUBA <ea>,An binary subtraction from an address
register
SUBI #n,<ea> subtraction of a constant
SUBQ #n,<ea> fast subtraction of a 3 bit constant
SUBX source,dest subtraction with transfer in X flag
TST <ea> test an operand and set N and Z flag
For the processing of whole numbers,the processor can operate with
BCD numbers.These are Binary Coded Decimal numbers,which means
that the processor is working with decimals.In this method,each
halfbyte contains only numbers from 0 to 9,so that these numbers
can be easily processed.For this method,the following instructions
are available:
Mnemonic Meaning
-----------------------------------------------------------------
ABCD source,dest addition of two BCD numbers
NBCD source,dest negation of a BCD number(nine
complement)
SBCD source,dest subtraction of two BCD numbers
Again,we recommend that you try this out yourself.Although
handling the BCD numbers is relatively easy,it can be rather
awkward at first.Be sure that you enter only BCD numbers for
source and destination,since the results are not correct
otherwise.
Next are the logical operations,which you might know from BASIC.
With these functions,you can operate on binary numbers bit for
bit.
Mnemonic Meaning
-----------------------------------------------------------------
AND source,dest logic AND
ANDI #n,<ea> logic AND with a constant
EOR source,dest exclusive OR
EORI #n,<ea> exclusive OR with a constant
NOT <ea> inversion of an operand
OR source,dest logic OR
ORI #n,<ea> logic OR wuth a constant
TAS <ea> check a byte and set bit 7
Single bits can also be manipulated by the following set of
instructions:
Mnemonic Meaning
----------------------------------------------------------------
BCHG #n,<ea> change bit n(0 is changed to 1 and vice
versa)
BCLR #n,<ea> clear bit n
BSET #n,<ea> set bit n
BTST #n,<ea> test bit n,result is displayed in Z
flag
These instructions are particularly important from the
manipulation and evaluation of data from peripherals.After all,in
this type of data,single bits are often very significant.You'll
come across this more in later chapters.
The processor can also shift and rotate an operand within itself
('n'indicates a register,'#'indicates a direct value which
specifies the number of shiftings)...
Mnemonic Meaning
----------------------------------------------------------------
AS n,<ea> arithmetic shift to the left(*2^n)
ASR n,<ea> arithmetic shift to the right(/2^n)
LSL n,<ea> logic shift to the left
LSR n,<ea> logic shift to the right
ROL n,<ea> rotation left
ROR n,<ea> rotation right
ROXL n,<ea> rotation left with transfer in X flag
ROXR n,<ea> rotation right with transfer in X flag
All these instructions allow you to shift a byte,a word or a long
word to the left or right.Its not too surprising that this is the
equivalentof multipling(dividing)the number by a power of 2.Here's
a little example to demonstrate why
Lets take a byte containing the value 16 as an example.In binary,
it looks like this:
%00010000 =16
Now,if you shift the byte to the left by inserting a 0 at the
right,you'll get the following result...
%00010000 shifted to the left equals
%00100000 =32,in effect 16*2
Repeated shifting results in repeated doubling of the number.Thus
if you shift the number n times,the number is multiplied by 2^n.
The same goes for shifting to the right.However,this operation as
a slight quirk:here's a sample byte with the value 5:
%00000101 =5,shifted once to the right equals
%00000010 =2
The answer in this case is not 2.5 as you might expect.The result
such a division is always a whole number,since any decimal places
are discarded.If you use the DIV instruction instead of shifting,
you'll retain the digits to the right of the decimal point.However
shifting is somewhat faster,and shifting can also receive long
words as results.
After explaining the principle of shifting,you still need to know
why more than two instructions are required for the procedure.Well
this is because there are several different types of shifting.
First,you must differentiate between shifting and rotating.In
shifting,the bit that is added to the left or the right side is
always a zero.In rotating,it is always a specific value that is
inserted.This means that with the ROR or the ROL instructions,the
bit that is taken out on one side is the one that is inserted on
the other.With the ROXR and the ROXL instructions this bit takes a
slight detour to the X flag.Thus,the content of the flag is
inserted into the new bit,while the old bit is loaded into the
flag.
Shifting as well,has two variations:arithmetic and logical
shifting.You've already dealt with logical shifting.In this
variation,the inserted bit is always a zero,and the extracted bit
is deposited in the C flag and in the X flag.
Although the highest bit,which always represents the sign,is
shifted in arithmetic shifting,the sign is still retained by ASR.
This has the advantage that when these instructions are used for
division,the operation retains the correct sign(-10/2 equals-5).
However,should an overflow or underflow cause the sign to change,
this change is noted in the V flag,which always indicates a change
in sign.With logical shifting this flag is always cleared.
Now to the instructions that allow you to move data.These are
actually the most important instructions for any processor,for how
else could you process data?
Mnemonic Meaning
------------------------------------------------------------------
EXG Rn,Rn exchange of two register contents(don't
confuse with swap)
LEA <ea>,An load an effective address in address
register An
LINK An,#n build stack range
MOVE source,dest carry value over from source to dest
MOVE SR,<ea> transfer the status register contents
MOVE <ea>,SR transfer the status register contents
MOVE <ea>,CCR load flags
MOVE USP,<ea> transfer the user stack point
MOVE <ea>,USP transfer the user stack point
MOVEA <ea>,An transfer a value to the address
register An
MOVEM Regs,<ea> transfer several registers at once
MOVEM <ea>,Regs transfer several registers at once
MOVEP source,dest transfer data to peripherals
MOVEQ #n,Dn quickly transfer a 8 bit constant to
the data register Dn
PEA <ea> deposit an address on the stack
SWAP Dn swap the halves of the register(the
upper 16 bits with the lower)
UNLK An unlink the stack
The LEA or PEA instructions are often used to deposit addresses in
an address register or on the stack.The instruction
LEA label,A0
loads the address of the label'label' into the address register
A0.In practice,this corresponds to
MOVE.L #label,A0
which is equivalent to
PEA label
All these instructions deposit the address of 'label' on the stack
The following instruction also does this:
MOVE.L #label,-(SP)
The LEA instruction becomes much more interesting when the label
is replaced by indirect addressing.Here's an example:
LEA 1(A0,D0),A1
The address that's produced by the addition of 1(direct value
offset)+A0+D0 is located in A1.To duplicate this instruction with
MOVE would be quite cumbersome.Take a look:
MOVE.L A0,A1
ADD.L D0,A1
ADDQ.L #1,A1
As you can see,the LEA instruction offers you quite some
interesting possibilities.
Those are all the instructions of the MC68000.Through their
combination using the diverse methods of addressing,you can create
a great number of different instructions,in order to make a
program as efficent as possible.
The following table is an overview of all MC68000 instructions
along with their possible addressing types and the influence of
flags.The following abbreviations are used:
x=legal s=source only d=destination only
-=not effected 0=cleared *=modified accordingly
1=set u=undermined P=privileged
Mnemonic 1 2 3 4 5 6 7 8 9 10 11 12 X N Z V C P
-----------------------------------------------------------------
ABCD x
ADD s s x x x x x x x s s s * * * * *
ADDA x x x x x x x x x x x x - - - - -
ADDI x x x x x x x x * * * * *
ADDQ x x x x x x x x x * * * * *
ADDX x x * * * * *
AND s x x x x x x x s s s - * * 0 0
ANDI x x x x x x x x - * * 0 0
ASL, ASR x x x x x x x x * * * * *
Bcc - - - - -
BCHG x x x x x x x x - - * - -
BCLR x x x x x x x x - - * - -
BRA - - - - -
BSET x x x x x x x x - - * - -
BSR - - - - -
BTST x x x x x x x x z x x - - * - -
CHK x x x x x x x x x x x - * u u u
CLR x x x x x x x x - 0 1 0 0
CMP x x x x x x x x x x x x - * * * *
CMPA x x x x x x x x x x x x - * * * *
CMPI x x x x x x x x - * * * *
CMPM x x x x x x x - * * *
cpGEN - - - - -
DBcc - - - - -
DIVS x x x x x x x x x x x - * * * 0
DIVU x x x x x x x x x x x - * * * 0
EOR x x x x x x x x - * * 0 0
EORI x x x x x x x x - * * 0 0
EORI CCR * * * * *
EORI SR * * * * *
EXG - - - - -
EXT - * * 0 0
EXTB - * * 0 0
ILLEGAL - - - - -
JMP x x x x x x x - - - - -
JSR x x x x x x x - - - - -
LEA x x x x x x x - - - - -
LINK x - - - - -
LSL, LSR x x x x x x x * * * 0 *
MOVE x s x x x x x x x s s s - * * 0 0
MOVEA x x x x x x x x x x x x - - - - -
MOVE to CCR x x x x x x x x x x x * * * * *
MOVE from SR x x x x x x x x - - - - - P
MOVE to SR x x x x x x x x x x x * * * * * P
MOVE USP x - - - - - P
MOVEM x s d x x x x s s - - - - -
MOVEP s d - - - - -
MOVEQ d - * * 0 0
MULS x x x x x x x x x x x - * * 0 0
MULU x x x x x x x x x x x - * * 0 0
NBCD x x x x x x x x * u * u *
NEG x x x x x x x x * * * * *
NEGX x x x x x x x x * * * * *
NOP - - - - -
NOT x x x x x x x x - * * 0 0
OR s x x x x x x x s s s - * * 0 0
ORI x x x x x x x x - * * 0 0
PEA x x x x x x x - - - - -
RESET - - - - - P
ROL, ROR x x x x x x x - * * 0 *
ROXL, ROXR x x x x x x x - * * 0 *
RTE - - - - - P
RTR * * * * *
RTS - - - - -
SBCD x x * u * u *
Scc x x x x x x x x - - - - -
STOP x - - - - -
SUB s s x x x x x x x s s s * * * * *
SUBA x x x x x x x x x x x x - - - - -
SUBI x x x x x x x x * * * * *
SUBQ x x x x x x x x x * * * * *
SUBX x x * * * * *
SWAP x - * * 0 0
TAS x x x x x x x x - * * 0 0
TRAP x - - - - -
TRAPV - - - - -
TST x x x x x x x x - * * 0 0
UNLK x - - - - -
Chapter 3.
---------
3.Working With Assemblers.
-------------------------
The instructions that you've learned so far are incomprehensible
to the MC68000 processor.The letters MOVE mean absolutely nothing
to the processor-it needs the instructions in binary form.Every
instruction must be coded in a word-which normally takes a lot of
work.
An assembler does this work for you.An assembler is a program that
translates the instructions from text into the coresponding binary
instructions.The text that is translated is called Mnemonic or
Memcode.Its a lot easier working with text instructions-or does
$4280 mean more to you than CLR.L D0?
This chapter is about working with assemblers.We'll describe the
following three:
ASSEM;
This is the assembler from the Amiga's development package.This
assembler is quite powerful,but it is clearly inferior to its two
fellow compilers in some areas.
AssemPro;
This is the Abacus assembler.It has a debugger in addition to the
assembler.This lets you test and correct programs.In our opinion,
it is the best one to use for writing and testing practice
programs.For this reason,we wrote the programs in this book with
this assembler.
KUMA-SEKA;
This is a popular assembler which also has a debugger.
All assemblers perform a similar task-they translate memcode,so
that you can write a runable program to disk.To test the program
directly,you need a debugger which is something Assem doesn't
have.
3.1.The Development Assembler.
-----------------------------
This assembler is a plain disk assembler.That means that it can
only assemble text files that are on disk and write the result
back to disk.You can't make direct input or test run the new
program.
You can call ASSEM from the CLI by typing ASSEM followed by
parameters that specify what you wish the assembler to do.
In the simplest case,you call it like this:
ASSEM Source -O Destination
Source is the filename of the file containing the program text.
Destination is the name of the file that contains the results of
assembling after the process is over.The "-O"means that the
following name is used for the object file.
There are several other parameters that can be passed.These are
written with their option(ie-O),so that the assembler knows what
to do with the file that you've told it to use.The following
possible options must be followed by a filename.
-O Object file
-V Error messages that occur during assembling are written
to a file.If this isn't given,the error messages appear
in the CLI window.
-L The output of the assembled program lines are sent to
this file.You can also use"PRT:"to have it printed.
-H This file is read in at the beginning of the assembled
file and assembled along with it.
-E A file is created that contains lines which have EQU
instructions.
-C This option isn't followed by a filename but by another
option.You can also use OPT to do this.The following
options are available:
OPT S A symbol table is created which contains all the
labels and their values.
OPT X A cross reference list is created(where labels
are used).
OPT W A number must follow this option.It sets the
ammount of workspace to be reserved.
The assembler creates an object file.This is not runnable.To make
it runnable,you need to call the linker,ALINK.This program can
link several assembled or compiled object files together to make a
runnable program.In the simplest case,you enter the following
instruction in the CLI:
ALINK Source TO Destination
Source is the object file produced by the assembler.Destination is
the name of the program.It can be started directly.
3.2.AssemPro.
------------
Abacus's AssemPro is a package which combines editor,assembler and
debugger in an easy to use package.
The AssemPro Program is divided into several windows-one for the
assembler,the editor,the debugger and several help functions.
Producing a program is very easy:
1) Write the program with the editor and then store it to disk.
2) Start the assembler,so that the program is translated.
3) If desired,start the debugger,load the program and test it.
Within the debugger you can work through parts of the program or
even through single commands.As after each one of these steps the
debugger shows you the state of the registers and flags in the
status register,you can easily try the programs presented in this
book.
You need to load AssemPro only once when working with machine
language programs.Thus you don't need to save back and to between
editor,assembler,linker and debugger.
AssemPro has an especially interesting function:the reassembler.
With this debugger function you are able to convert runnable
programs into the source text of the program.Once you have made
the source text,you can edit the program using the editor and
assemble it again.AssemPro is equipped with functions other
assemblers miss.There are however,some differences you should know
about.As many programs you see were written for the K-SEKA,be
aware of one difference:the EVEN command.AssemPro uses the ALIGN
instruction.
Note,that when entering and assembling one of the programs in
AssemPro you must make sure that you place an END directive at the
end of the source text.
The following is an introduction into working with AssemPro and
the programs in this book.
Start AssemPro normally,next click on the editor window and start
typing in your program.If the program is on disk already,load it
by selecting the appropriate menu or by using the key combination
right <AMIGA> key and <o>.To do this you only need to ckick on the
filename in the displayed requester and click on the OK gadget.
Once you have typed in or loaded the program into the editor,you
can assemble it.It is best to save your source before assembling.
You assemble your program by clicking on the assembler window
displayed above the editor window and pressing <AMIGA> and <a>.You
can then choose how to locate your program in the memory.Remember
that data used by the co-processors must be located in CHIP RAM.
By clicking OK you start the assembler process.If you additionally
select "breakable",you can cancel the process by pressing both
shift keys.If any error occurs during assembling,AssemPro uses a
window to tell you this.Use this window to correct the error and
continue with "Save and try again".
Now the runnable program is located in the Amiga's memory.Use the
menu item "Save as"to save it on disk.If you want to store it on
RAM disk,click the given filename and enter RAM: in front of this
name.In addition you can click on the menu item "ICON"and choose
if you only want the program itself on disk but the icon too.Use
this icon to start the program at a later time from Workbench.
To test-run the program,you move the debugger window to the
foreground of the screen(for instance by clicking on the back
gadget).Use "Load"in the debugger menu or <AMIGA> <o> to call the
select file window,where you select the saved program.The program
is then loaded into the memory and its shown disassembled.
The highlighted line(orange)represents the current state of the
program counter.This is the line where the processor reads its
next instruction,provided you tell the processor so.There are
three ways to do so.
The first one is to start the program with "Start".This
alternative does not enable you to stop the program if anything
goes wrong.
The second possibility,"Start breakable"is better in this respect.
After the program starts,it continuously displays the registers
contents on the left side of the window.In addition to that you
can cancel the process by pressing <ESC>.Note that this only works
if your program doesn't use the <ESC>key itself.
The third possibility enables you to only partly run your program.
You can do this by stepping through the program or by placing
breakpoints throughout the program.You place these by clicking on
the desired address and then pressing <AMIGA> <b>."BREAKPOINT"is
displayed where the command was displayed before.If you start the
program now,it stops whenever it comes across any breakpoints.
You can start a small part of the program by moving the mouse
pointer to the orange line,clicking the left button and holding it
down while you drag the mouse pointer downward.If you release the
button,the processor works through this part of the program,
stopping at the line,where you positioned the mouse pointer.This
is a very useful method to step by step test a program.
AssemPro as another helpful window:the Table.This window lists the
valid address methods for instructions and the parameters of Amiga
functions.This is extremely helpful whenever you are not sure
about one of the instructions.
3.3.The K-SEKA Assembler.
------------------------
The SEKA assembler,from KUMA,has a simple text editor and a
debugger in addition to the assembler.This program is controlled
by simple instructions and it is easy to use.It is also multi-
functional and quick,so it is great for small test and example
programs.You can use it to write bigger programs once you've got
use to the editor.Now lets look at the editor.
To load a program as source code(text)into the editor,enter"r"
(read).The program asks you for the name of the file with the
"<FILENAME>"prompt.You then enter the name of the text file.If you
created the file with SEKA,the file is stored on disk with".s" on
the end of its name.You don't need to include the".s"when you load
the file.Thats taken care of automatically.("s"stands for source.)
You can store programs you've just written or modified by using
the"w"instruction.The program asks you for the name.If you enter
"Test",the file is written to the disk with"Test.s"as its name.
This is a normal text file in ASCII format.
There are two ways to enter or change a programs:using the line
editor or the screen editor.You can enter the second by hitting
the <ESC>key.The upper screen section is then reserved for the
editor.You can move with the cursor keys and change the text
easily.The lines that you enter are inserted into the existing
text and automatically numbered.By hitting the <ESC>key again,you
leave the screen editor.
There's really not much to say about this editor.It's really just
for simple insertions and changes.Other functions are called in
normal instruction mode,the mode in which">"is the input prompt.
The following instructions are available to you for text editing
(<n>stands for a number.The meaning of the instructions is in
parenthesis.)
Instruction Function
----------------------------------------------------------------
t(Target) Puts the cursor on the highest line in the
text.
t<n> Puts the cursor on line n.
b(Bottom) Puts the cursor on the last line in the text.
u(Up) Go up one line.
u<n> Go up n lines.
d(Down) Go down one line.
d<n> Go down n lines.
z(Zap) Deletes the current line.
z<n> Deletes n lines starting at the cursor line.
e(Edit) Lets you edit the current line(and only that
line).
e<n> Edit from line n.
ftext(Find) Searches for the text entered starting at the
current line.The case of a letter makes a
difference,so make sure to enter it correctly.
Blanks that appear after the f are looked for
as well!
f Continues searching beyond the text that was
previously given.
i(Insert) Starts the line editor.Now you can enter a
program line by line.However,you can't use the
cursor to move into another line.Line numbers
are generated automatically.The lines that
follow are moved down,not erased.
ks(Kill Source) The source text is deleted if you answer"y"
when the program asks if you are sure.Otherwise
nothing happens.
o(Old) Cancels the "ks"function and saves the old text
p(Print) Prints the current line.
p<n> Prints n lines starting at cursor line.
Those are the K-SEKA's editor functions.In combination with the
screen editor,they allow for simple text editing.You can,for
example,delete the current line(and other lines)while working in
the screen editor by hitting <ESC> to get into instruction mode
and then entering"z"(or "z<n>").
If you'd like to edit all lines that contain "trap",for example,
you can do the following:
-Jump to the beginning of the text using "t"
-Search for a "trap"instruction by entering "ftrap" in the
first line.
-Press <ESC> and edit the line.
-Press <ESC> again to get into instruction mode.
-Search using "f",<ESC>,etc.until you get to the end of the
text.
This sounds very clumsy,but in practise it works quite well and
goes quickly.Experiment with the editor bit,so you can get use to
it.
Now here are the instructions for working with disks:
Instruction Function
-----------------------------------------------------------------
v(View files) Look at the disk's directory.You can also
include the disk drive or subdirectory
that interests you.For example,"vc"causes
the "c"subdirectory to be listed and makes
it the current directory.
kf(Kill file) The program asks for the name of the file.
The file is deleted(and you aren't asked
if your sure either-so be careful).
r(Read) After inputting this instruction,you'll be
asked which file to load(FILENAME>).The
file that you specify is then loaded.If
only "r"is entered,a text file is loaded
in the editor.
ri(Read Image) Loads a file into memory.After you've
entered the filename,SEKA asks for the
address the file should begin at in memory
(BEGIN>)and the highest address that
should be used for the file(END>).
rx(Read from Auxillary) This works just like the "ri"function
except that it reads from the serial port
instead of from disk(You don't need a file
name).
rl(Read Link file) This instruction reads in a SEKA created
link file.First you'll be asked if you are
sure,because the text buffer is erased
when the link file is loaded.
w(Write) After entering this instruction,you'll be
asked for the name of the file the text
should be written to.A".s"is automatically
appended to the name,so that it can be
recognized as a SEKA file.
wi(Write Image) Stores a block of memory to disk after the
name,beginning and end are entered.
wx(Write to Auxillary) This is similar to"wi";the only difference
is that the output is to the serial inter-
face.
wl(Write Link file) Asks for the name and then stores a link
file that was assembled with the"I"option
to disk.If this isn't available,the
message "* * Link option not specified"
appears.
Once you've typed in or loaded a program,you can call the
assembler and have the program translated.Just enter"a"to do so.
You'll then be asked which options you want to use.If you enter a
<RETURN>,the program is assembled normally-ie the results of
translating a program in memory is stored in memory.Then the
program can be executed straight away.
You can enter one or more of the following options,however:
v The output of the results goes to the screen.
p or
e goes to the printer with a title line.
h The output stops after every page and waits for a key
stroke.This is useful for controlling output to the screen
or for putting new sheets of paper in the printer.
o This option allows the assembler to optimize all possible
branch instructions.This allows the program code to be
shorter than it would otherwise be.Several messages appear
but you can ignore them.
l This option causes linkable code to be produced.You can
save it with the"wl"instruction and read it with the "rl"
instruction.
A symbol table is included at the end of the listing if desired.
The table contains all labels and their values.It also contains
macro names.A macro allows several instructions to be combined in
to a single instruction.
For example,suppose you wrote a routinethat outputs the text that
register A0 points to.Every time you need to use the routine,you
must type:
lea text,a0 ;pointer to text in A0
bsr pline ;output text
You can simplify this by defining a macro for this function.To do
this,put the following at the beginning of the program:
print:macro ;Macro with the name "Print"
lea ?1,a0 ;Parameter in A0
bsr pmsg ;Output text
endm ;End of macro
Now,you can simply write the following in your program:
print text ;Output text
This line is replaced using the macro during assembly.The
parameter "text"is inserted where "?1"appears in the macro.You can
have several parameters in a macro.You give them names like "?2",
"?3",etc...
You can also decide whether you'd like to see the macros in the
output listing of the assembler.This is one of the pseudo-ops that
are available in the assembler.The SEKA assembler has the
following pseudo-ops:
dc Defines one or more data items that should appear in
this location in the program.The word length can be
specified with .B,.W,or .L-and if this is left off, .B
is used.Text can be entered in question marks or
apostrophes.For example:dc.b "Hello",10,13,0
blk Reserves a number of bytes,words or long words,depending
on whether .B,.W,or .L is chosen.The first parameter
specifies the number of words to be reserved.The second
(which is optional)is used to fill the memory area.For
example:blk.w 10,0
org The parameter that follows the org instruction is the
address from which the (absolute) program should be
assembled.For example: org $40000
code Causes the program to be assembled in relative mode,the
mode in which a program is assembled starting at address
0.The Amiga takes care of the new addressing after the
program is loaded.
data This means that from here on only data appear.This can
be left out.
even Makes the current address even by sometimes inserting a
fill byte.
odd The opposite of even-it makes the address odd.
end Assembling ends here.
equ or Used for establishing the value of a label
= For example: Value=123 or Value:equ 123
list Turns the output on again(after nlist).You can use the
following parameters to influence the output:
c Macro calls
d Macro definitions
e Macro expansion of the program
x Code expansions
For example: list e
nlist Turns off output.You can use the same parameters here as
with "list".
page Causes the printer to execute a page feed,so that you'll
start a new page.
if The following parameter decides whether you should
continue assembling.If it is zero,you won't continue
assembling.
else If the "if"parameter is zero,you'll begin assembling
here.
endif End of conditional assembling.
macro Start of a macro definition.
endm End of macro definition.
?n The text in the macro that is replaced by the nth
parameter in the calling line.
?0 Generates a new three digit number for each macro call-
this is very useful for local labels.
For example: x?0:bsr pmsg
illegal Produces an illegal machine language instruction.
globl Defines the following label as globel when the "I"option
of the assembler is chosen.
Once you've assembled your program,the program code is in memory.
Using the "h"instruction,you can find out how large the program is
and where it is located in memory.The beginning and end address is
given in hex and the length in decimal(according to the last
executed operations):
Work The memory area defined in the beginning
Src Text in memory
RelC Relocation table of the program
RelD Relocation table of the memory area
Code Program code produced
Data The program's memory area
You'll find program in memory at the location given by Code.It's a
pain to have to enter this address whenever you want to start the
program.It make's good sense to mark the beginning of the program
with a label(for example,"run:").You can use the "g"instruction to
run the program as follows:
g run
The"g"(GO)instruction is one of SEKA's debugger instrucions.Heres
an overview:
x Output all registers.
xr Output and change of registers(ie xd0)
gn Jump to address n.You`ll be asked for break points,addresses
at which the program should be terminate.
jn This is similar to the one above-a JSR is used to jump into
the program.The program must end with a RTS instruction.
qn Output the memory starting at address n.You can also specify
the word length.For example: q.w $10000
nn Disassembled output starting at address n.
an Direct assembling starting at address n.Direct program
instructions are entered.
nn Modify the contents of memory starting at address n.Here too
the word length can be given.You can terminate input with
the <ESC> key.
sn Executes the program instruction that the PC points to.After
you enter this instruction,n program steps are executed.
f Fill a memory area.You can choose the word width.All the
needed parameters are asked for individually.
c Copies one memory area to another.All the needed parameters
are asked for individually.
? Outputs the value of an expression or a label.
For example: ?run+$1000-256
a Sets an instruction sequence that is passed to the program
when it starts as if it were started from CLI with this
sequence.
! Leaves the SEKA assembler after being asked if your sure.
You saw some of the calculations like SEKA can make in the "?"
example.You can also use them in programming.The folowing
operations work in SEKA:
+ Addition
- Subtraction
* Multiplication
/ Division
& Logic AND
! Logic OR
~ EXclusive OR (XOR)
These operations can also be combined.You can choose the counting
system.A "$"stands for hexadecimal,"@"for octal,and "%"for binary.
If these symbols aren`t used,the number is interpreted as a
decimal number.
Lets go back to the debugger.As mentioned,after entering "g
address",you`ll be asked for break points.You can enter up to 16
addresses at which the program halts.If you don`t enter break
points,but instead hit <RETURN>,the program must end with an
ILLEGAL instruction.If it ends instead with a RTS,the next return
address from the stack is retrieved and jumped to.This is usually
address 4 which causes SEKA to come back with "**Illegal
Instruction at $000004",but theres no guarantee that it will.Your
computor can end up so confused that it can`t function.
The SEKA program puts an ILLEGAL instruction in the place
specified as break points after saving the contents of these
locations.If the processor hits an illegal instruction,it jumps
back to the debugger by using the illegal instruction vector that
SEKA set up earlier.Then SEKA repairs the modified memory
locations and then displays the status line.Here you can find out
where the program terminated.
Using break points is a good technique for finding errors in the
program.You can,for example,put a break point in front of a
routine that you`re not sure about and start the program.When the
program aborts at this spot,you can go through the routine step by
step using the "s"option.Then you can watch what happens to the
status line after each instruction and find the mistake.
Program errors are called bugs.That`s why the program that finds
them is called a debugger.
Chapter 4.
---------
4.Our First Programs.
--------------------
You`re getting pretty far along in your knowledge of machine
language programming.In fact,you`re to the point where you can
write programs,and not just programs for demonstration purposes,
but ones that serve a real function.We`re assuming that you have
the AssemPro assembler and have loaded it.
If you`re using a different assembler,a few things must be done
differently.We covered those differences already in the chapter on
the different assemblers.
We`ve written the example programs as subroutines so they can be
tried out directly and used later.After assembling the program,you
can put the desired values in the register.Then you can either
single-step thru the programs or run the larger programs and
observe the results in the registers directly.(using the SEKA
assembler you can type "j program_name"to start the program.Then
you can read the results from the register directly,or use "q
address"to read it from memory.)
Lets start with an easy example,adding numbers in a table.
4.1.Adding Tables.
-----------------
Imagine that you have numbers in memory that you'd like to add.
Lets assume that you have five numbers whose length is one word
each.You want their sum to be written in register D0.The easiest
way to do this is:
;(4.1A)
adding1:
clr.l D0 ;Erase D0 (=0)
move table,d0 ;First entry in D0
add table+2,d0 ;Add second entry
add table+4,d0 ;Add third entry
add table+6,d0 ;Add fourth entry
add table+8,d0 ;add fifth entry
rts ;Return to main program
table: dc.w 2,4,6,8,10,
end
Try out the program using the debugger by single stepping thru the
program until you get to the RTS instruction(left Amiga T).(SEKA
owners use "j adding1").You see that data register D0 really
contains the sum of the values.
The method above only reads and adds numbers from a particular set
of addresses.The Amigas processor has lots of different sorts of
addressing modes that give us a shorter and more elegant solution.
Lets add a variable to the address of the table,so that the
program can add different tables.
Lets put the addresses of the table in an address register(for
example, A0)instead.This register can be used as a pointer to the
table.You must use move.l since only long words are relocatable.By
using a pointer to a table you can use indirect addressing.You can
change the expression "table+x" to"x(A0)".
;(4.1B)
adding1:
clr.l D0 ;Erase D0 (=0)
move.l #table,a0 ;Put table addresses in A0
move 0(a0),d0 ;Put first entry in d0
add 2(a0),d0 ;Add second entry
add 4(a0),d0 ;Add third entry
add 6(a0),d0 ;Add forth entry
add 8(a0),d0 ;Add fifth entry
rts ;Return to main program]
table: dc.w 2,4,6,8,10
end
Assemble this program,load it into the debugger.Then single step
(left Amiga T)thru this program and you'll see that this program
adds five numbers in order just like the last one.The reason you
used a step size of two for the ofset is that words are two bytes
long.AssemPro also defaults to relocate code so that you must move
#table as a long word.
Lets improve the program more by using "(a0)+"instead of "x(a)".
This way,every time you access elements of the table,the address
register A0 is automatically incremented by the number of bytes
that are read(in this case two).The difference between this and
the last example is that here the registers contents are modified.
The pointer is to the next unused byte or word in memory.
Lets make it even better.Lets make the number of words to be added
to a variable.You'll pass the number in register D1.Now you need
to do a different sort of programming,since you can't do it with
the old methods.
Lets use a loop.You need to add D1 words.You can use(a0)+ as the
addressing method(address register indirect with post increment),
since this automatically gets you to the next word.
Now for the loop.You'll have D1 decremented by one every time the
contents of the pointer are added.If D1 is zero,then you're done.
Otherwise,you need another addition.The program looks like this:
;(4.1C)
adding2:
clr.l d0 ;Erase D0
move.l #table,a0 ;Put table addresses in A0
move #$5,d1 ;Put number of entries in D1
loop: ;Label for loop beginning
add (a0)+,d0 ;Add a word
subq #1,d1 ;Decrement counter
bne loop ;Continue if non-zero
rts ;Else done
table: dc.w 2,4,6,8,10
end
Lets take a close look at this program.Load the pointer A0 with
the addresses of the data and the counter D1 with the number of
elements.Then you can single step thru the program and watch the
results.Make sure not to run the final command,the RTS command,
because otherwise a return address is popped from the stack,and
the results of this are unpredictable.(SEKA owners can use"x pc"
to point the program counter to "adding2".You can then step thru
the program by using the "s"command and watch the results).
To finish up this example,your assigning a little homework.Write
the program so that it adds single bytes or long words.Try to
write a program that takes the first value in a table and
subtracts the following values.For example,the table
table: dc.w 50,8,4,6
should return the value 50-8-4-6, ie 32 ($20).
4.2.Sorting a Table.
-------------------
Lets keep working with tables.You don't want to just read data
from one this time.You want to change it.You'll assort the table
in assending order.
You need to decide how to do the sorting.The simplest method is to
do the following.
Compare the first and the second value.If the second value is
larger than the first one,things are OK so far.Do the next step,
compare the second and third values,and so on.If you come to the
final pair and in each case the preceding value was smaller than
the following value,then the sorting is done(it was unnescessary).
If you find a pair where the second value is smaller than the
first,the two values are exchanged.You then get a flag(here let's
use a register)that is checked once you're done going thru the
table.If it is set,the table probably isn't completely sorted.You
then erase the flag and start again from the beginning.If the flag
is still zero at the end,the sorting is complete.
Now let's write a program to do this.First let's figure out the
variables you need.You'll use registers for the variables.You need
a pointer to the table you're sorting(A0),a counter(D0)and a flag
(D1).While the program is running,change these values,so you'll
need two more registers to store the starting values(address and
the number of the table entries).You'll use A1 and D2.
Let's start writing the program,each section will be written and
then explained.Then the complete program will be given.You put the
tables address in A1 and the number of entries in D2.
;(4.2A) part of sort routine
sort: ;Start address of the program
move.l #table,a1 ;Load pointer with address
move.l a1,a0 ;Copy pointer to working register
move.l #5,d2 ;Number in the counter
move.l d2,d0 ;Copy number of elements
subq #2,d0 ;Correct conter value
clr d1 ;Erase flag
table: dc.w 3,6,9,5
end
Now the preparations are complete.The pointer and the counter are
ready and the flag is cleared.The counter is decremented by two
because you want to use the DBRA command(take one off)and only X-1
comparrisons are needed for X numbers(take off one more).
Next let's write the loop that compares the values.You compare one
word with another.It looks like this:
loop:
move 2(a0),d3 ;Next value in register D3
cmp (a0),d3 ;Compare values
You need to use register D3 because CMP (A0),2(A0) isn't a legal
choice.If the second value is greater than or equal to the first
value,you can skip an exchange.
bcc noswap ;Branch if greater than or equal
;to
Now you need to do the exchanging(unfortunatly you can't use EXC
2(a0),(a0) since this form of addressing does not exist).
doswap:
move (a0),d1 ;Save first valus
move 2(a0),(a0) ;Copy second into first word
move d1,2(a0) ;Move first into second
moveq #1,d1 ;Set flag
noswap:
Now increment the counter and continue with the next pair.You do
this until the counter is negative.
addq.l #2,a0 ;Pointer+2
dbra d0,loop ;Continuing looping until the end
Now you'll see if the flag is set.You start again at the beginning
if it is.
tst d1 ;Test flag
bne sort ;Not finished sorting yet!
rts ;Otherwise done.Return
If the flag is zero,you're done and the subroutine ends.You jump
back to the main program using the RTS command.
Now a quick overview of the complete program.
;(4.2B)
sort: ;Start address of the program
move.l #table,a1 ;Load pointer with address
move.l a1,a0 ;Copy pointer to working register
move.l #5,d2 ;Number in the counter
move.l d2,d0 ;Copy number of elements
subq #2,d0 ;Correct counter value
clr d1 ;Erase flag
loop:
move 2(a0),d3 ;Next value in register D3
cmp (a0),d3 ;Compare values
bcc noswap ;Branch if greater than or equal to
doswap:
move (a0),d1 ;Save first value
move 2(a0),(a0) ;Copy second into first word
move d1,2(a0) ;Move first into second
moveq #1,d1 ;Set flag
noswap:
addq.l #2,a0 ;Pointer+2
dbra do,loop ;Continue looping until the end
tst d1 ;Test flag
bne sort ;Not finished sorting yet!
rts ;Otherwise done.Return
table:
dc.w 10,8,6,4,2 ;When finished acceding
end
To test this subroutine,assemble the routine with AssemPro,save it
and then load it into the debugger.The table is directly after the
RTS,notice its order.Set a breakpoint at the RTS,select the
address with the mouse and press left-Amiga-B sets a breakpoint in
AssemPro.Start the program the redisplay the screen by selecting
"Parameter-Display-Dissassem-bled"and examine the order of the
numbers in the table,they should now be ascending order.
You use several registers in this example for storing values.
Usually in machine language programming your subroutines cannot
change any register or can only change certain registers.For this
reason,there is a machine language command to push several
registers onto the stack at the same time.This is the MOVEM ("MOVE
multiple")command.If you insert this command twice in your program
then you can have the registers return to the main program with
the values they had when the subroutine was called.To do this,you
need one more label.Let's call it"start";the subroutine is started
from here.
start:
movem.l d0-d7/a0-a6,-(sp) ;save registers
sort:
etc...
...
...
bne sort ;not finished sorting yet!
movem.l (sp)+,d0-d7/a0-a6 ;retrieve registers
rts ;finished
The powerful command moves several registers at the same time.You
can specify which registers should be moved.If you want to move
the D1,D2,D3,D7,A2 and A3 registers,just write
movem.l d1-d3/d7/a2-a3,-(sp)
Before you quit sorting,do one little homework assignment.Modify
the program,so that it sorts the elements in descending order.
4.3.Converting Number Systems.
-----------------------------
As we mentioned in the chapter on number systems,converting
numbers from one base to another can be rather difficult.There is
another form of numeric representation-as a string that can be
entered via the keyboard or output on the screen.
You want to look at some of the many conversions possible and
write programs to handle the task.You'll start by converting a hex
number into a string using binary numbers and then print the value
in hex.
4.3.1.Converting Hex To ASCII.
-----------------------------
First you need to set the start and finish conditions.In this
example,let's assume that data register D1 contains a long word
that should be converted into a 8-digit long string of ASCII
characters.You'll write it to a particular memory location so that
you can output it later.
The advantage of using hex instead of decimal is pretty clear in
this example.To find out the hexadecimal digit for a particular
spot in the number,you just need to take the corresponting 4 bits
(half byte)and do some work on it.A half byte(also called a
nibble)contains one hex digit.
You'll work on a half byte in D2.To convert this to a printable
character,you need to use the correct ASCII code.The codes of the
16 characters that are used as hex digits are the following:
0 1 2 3 4 5 6 7 8 9 A B C D E F
$30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $41 $42 $43 $44 $45 $46
To convert the digits 0-9,you just need to add $30.For the letters
A-F that correspond to the values 10-15,you need to add $37.The
program to evaluate a half byte must make a destinction between
values between 0 and 9 and those between A and F and add either
$30 or $37.
Now let's write a machine language subroutine that you'll call for
each digit in the long words hex representation.
nibble:
and #$0f,d2 ;just keep low byte
add #$30,d2 ;add $30
cmp #$3a,d2 ;was it a digit?
bcs ok ;yes:done
add #7,d2 ;else add 7
ok:
rts ;done
This routine converts the nibble in D2 to an ASCII character that
corresponds to the hex value of the nibble.To convert an entire
byte,you need to call the routine twice.Here is a program to do
this.The program assumes that A0 contains the address of the
buffer that the characters are to be put in and that D1 contains
the byte that is converted.
;(4.3.1a) bin-hex
; ;your program
lea buffer,a0 ;pointer to buffer
move #$4a,d1 ;byte to be converted(example)
bsr byte ;and convert
rts
; ... ;more of your program
byte:
move d1,d2 ;move value into d2
lsr #4,d2 ;move upper nibble into lower nibble
bsr nibble ;convert d2
move.b d2,(a0)+ ;put character into buffer
move d1,d2 ;value in d2
bsr nibble ;convert lower nibble
move.b d2,(a0)+ ;and put it in buffer
rts ;done
nibble:
and #$0f,d2 ;just keep low byte
add #$30,d2 ;add $30
cmp #$3a,d2 ;was it a digit?
bcs ok ;yes:done
add #7,d2 ;else add 7
ok:
rts ;done
buffer:
blk.b 9,0 ;space for long word data
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it intoi the debugger.Next set a breakpoint
at the first RTS,to set the breakpoint in AssemPro select the
correct address with the mouse and press the right-Amiga-B keys.
Start the program and watch the contents of D2,it is first $34
(ASCII 4)and finally $41(ASCII A).Select "Parameter-Display-HEX-
Dump"and you'll see that 4A has been moved into the buffer.
This is how the routine operates.First,you move the value that you
wish to convert into D2.Then you shift the register four times to
the right to move the upper nibble into the lower four bits.After
the subroutine call,you use "move.b d2,(a0)+"to put the HI nibble
in the buffer.Then the original byte is put in D2 again.It is
converted.This gives us the LO nibble as an ASCII character in D2.
You put this in the next byte of the buffer.
The buffer is long enough to hold a long word in characters and
closing the null byte.The null byte is usually required by screen
output routines.Screen output will be discussed in a later
chapter.Now let's worry about converting a long word.
When converting a long word,you need to be sure to deal with the
nibbles in the right order.Before calling the "nibble"routine for
the first time,you need to move the upper nibble into the lower 4
bits of the long word.You need to do this without losing anything.
The LSR command isn't very good for this application.If you use it
you'll lose bits.Its better to use the rotation commands like ROR
or ROL,since they move the bits that are shifted out,back in on
the other side.
If you shift the original long word in D1 four times to the left,
the upper four bits are shifted into the lower four bits.Now you
can use our "nibble"routine to evaluate it and then put the
resulting ASCII character in the buffer.You repeat this eight
times and the whole long word has been converted.You even have D1
looking exactly the way it did before the conversion process began
;(4.3.1B) bin-hex-2
hexlong:
lea buffer,a0 ;pointer to the buffer
move.l #$12345678,d1 ;data to convert
move #7,d3 ;counter for the nibbles:8-1
loop:
rol #4,d1 ;move upper nibble into lower
move d1,d2 ;write in d2
bsr nibble ;and convert it
move.b d2,(a0)+ ;character in buffer
dbra d3,loop ;repeat 8 times
rts ;finished!
nibble:
and #$0f,d2 ;just keep low byte
add #$30,d2 ;add $30
cmp #$3a,d2 ;was it a digit?
bcs ok ;yes:done
add #7,d2 ;else add 7
ok:
rts ;done
buffer:
blk.b 9,0 ;space for long word,null byte
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the first RTS,to set the breakpoint in AssemPro select the correct
address with the mouse and press the right-Amiga-B keys.Start the
program and when it is finished redisplay the output by selecting
"Parameter-Display-HEX-dump"so you can examine the new buffer
contents.
You'll find that there's an error in the program-the buffer
contains the digits "56785678"instead of "12345678".Try to find
the error.
Have you found it?This is the sort of error that causes you to
start pulling your hair out.This sort is hard to find.The
assembler assumes that the rotation operation should be done on a
word,because the ".l"was left off.As a result,only the lower word
of D1 was rotated-so you get the same value twice.If you change
the "rol.l",things work just right.
The error shows how easy it is to convert the program above into
one that converts four digit hex numbers into ASCII characters.
Just leave off the ".l"on the "rol"command and change the counter
from seven to three.The program is done.
Now for a little homework:change the program so that it can handle
six digit hex numbers(D1 doesn't nescessarily have to stay the
same...)!
Now lets look at a different conversion problem:converting a four
digit decimal number.
4.3.2.Converting Decimal To ASCII.
---------------------------------
It's not quite as easy to convert decimal as hex.You can't group
the bits to form individual digits.You need to use another method.
Lets look at how a decimal number is constructed.In a four digit
number,the highest place is in the thousands place,the next is the
hundreds place,etc...
If you have the value in a register and divide by 1000,you'll get
the value that goes in the highest place in the decimal number.
Since the machine language command DIV not only gives us the
result of the division but also gives us the remainder,you can
work out the remainder quite easily.You divide the remainder by
100 to find the hundreds place,divide the remainder by 10 and get
the tens place,and the final remainder is the ones place.
This isn't so hard after all!Heres the program that follows the
steps above to fill the buffer with D1's ASCII value.
main:
lea buffer,a0 ;pointer to the buffer
move #1234,d1 ;number to convert
jsr deci_4 ;test subroutine
illegal ;room for breakpoint
deci_4: ;subroutine-four digit numbers
divu #1000,d1 ;divide by 1000
bsr digit ;evaluate result-move remainder
divu #100,d1 ;divide by 100
bsr digit ;evaluate result and move
divu #10,d1 ;divide by 10
bsr digit ;evaluate result-move remainder
;evaluate the remainder directly
digit:
add #$30,d1 ;convert result into ASCII
move.b d1,(a0)+ ;move it into buffer
clr d1 ;erase lower word
swap d1 ;move the remainder down
rts ;return
buffer:blk.b 5,0 ;reserve bytes for result
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the illegal instruction.To set the breakpoint in AssemPro select
the correct address with the mouse and press the right-Amiga-B
keys.This breakpoint stops the program.Start the program and when
it is finished redisplay the output by selecting"Parameter-Display
-HEX-dump"so you can examine the ASCII values now in the buffer.
You use a little trick in this program that is typical for machine
language programming.After calling"digit"three times from the sub-
routine"deci_4",you go right into the"digit"subroutine.You don't
use a BSR or JSR command.Once the processor hits the RTS command,
it returns to the main program,not the "deci_4"subroutine.Doing
this,you save a fourth "bsr digit"command and an "rts"command for
the "deci_4"routine.
Try the program out.Make sure that you use values that are smaller
than 9999,because otherwise strange things can happen.
Now let's reverse what you've been doing and convert strings into
binary numbers.
4.3.3.Converting ASCII To Hex.
-----------------------------
In a string,each hex digit represents a half byte.You just need to
write a program that exactly reverses what the hex conversion
program did.
You have two choices
1. The number of hex digits is known in advance
2. The number is unknown
The first is easier to program,but has the disadvantage that if,
you assume the strings are four digits in length and want to enter
the value 1,you must enter 0001.That is rather awkward,so you'll
use the second method.
Let's convert a single digit first.You'll pass a pointer to this
digit in address register A0.You want the binary value to come
back in data register D0.
The program looks like this:
move.l #string,a0 ;this example
jsr nibblein ;test routine
nop ;set breakpoint here
nibblein: ;*convert the nibble from (A0)
clr.l d0 ;erase D0
move.b (a0)+,d0 ;get digit,increment A0
sub #'A',d0 ;subtract $41
bcc ischar ;no problem:in the range A-F
add #7,d0 ;else correct value
ischar:
add #10,d0 ;correct value
rts
string:dc.b 'B',0 ;character to convert
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the first NOP,to set the breakpoint in AssemPro select the correct
address with the mouse and press the right-Amiga-B keys.Start the
program and watch the contents of D0.
Let's see how the program works.A0 points to a memory location
that contains the character "B"that is represented by the ASCII
value $42.This number is loaded into D0 right after this register
is erased.
After subtracting $41,you end up with the value $1.Now you're
almost done.Before returning to the main program,you add 10 to get
the correct value 11,$B.
If the buffer has a digit in it,the subtraction causes the
register to become negative.The C flag is set.Let's take the digit
5 as an example.
The ASCII value of 5 is $35.After subtracting $41,you end up with
-12 and the C flag is set.In this case,you won't branch with the
BCC command.Instead you'll add 7 to get -5.Then 10 is added,and
you end up with 5.Done!
The routine as a disadvantage.If an illegal character is given,one
that doesn't represent a hex digit,you'll get some nonsense result
Let's ignore error checking for the moment though.
Let's go on to multi-digit hex numbers.The first digit that you
convert has the highest value and thus represents the highest
nibble.To allow for this and to allow for an arbitrarily long
number(actually not arbitrarily long,the number should fit in a
long word-so it can only be eight digits long),you'll use a trick.
Take a look at the whole program.It handles the calculations and
puts the result in D1.It assumes that A0 is a pointer to a string
and that this string is ended by null byte.
hexin: ;converting a hex number
clr.l d1 ;first erase D1
move.l #string,a0 ;address of the string in A0
jsr hexinloop ;test subroutine
nop ;set breakpoint here
hexinloop:
tst.b (a0) ;test digit
beq hexinok ;zero,then done
bsr nibblein ;convert digit
lsl.l #4,d1 ;shift result
or.b d0,d1 ;insert nibble
bra hexinloop ;and continue
hexinok:
rts
nibblein:
clr.l d0 ;convert the nibble from (A0)
move.b (a0)+,d0 ;get digit,increment A0
sub #'A',d0 ;subtract $41
bcc ischar ;no problem:in range A-F
add #7,d0 ;else correct value
ischar:
add #10,d0 ;correct value
rts
string:DC.B "56789ABC',00 ;eight digit string,null byte
;to be converted
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the NOP,to set the breakpoint in AssemPro select the correct
address with the mouse and press the right-Amiga-B keys.Start the
program and watch the contents of D1,the hex value is placed in
this register.
The trick is to shift left four times,to shift one nibble.In this
way,the place of the last digit is incremented by one and there is
room for the nibble that comes back from the "nibblein"routine.The
program uses the TST.B instruction to check for the null byte at
the end of the string,when it encounters the null byte the program
ends.The result is in the D1 long word already!
To do some error checking,you need to make some changes in the
program.You'll do this right after you come back from the"nibblin"
routine with the value of the current character.
If the value in D0 is bigger than $F,there is an error.You can
detect this in several ways.You chose the simplest one-you'll use
CMP #$10,D0 to compare D0 with $10.If it smaller,then the C flag
is set(since CMP uses subtraction)and everything is fine.If C is
zero,there is an error.
You can use this trick to skip the test for a null byte,since its
an invalid character as well.The program looks like this:
;(4_3_3C) hex-conv2 optional disk name
hexin: ;converting a hex number
clr.l d1 ;first erase D1
move.l #string,a0 ;address of string in A0
jsr hexinloop ;test subroutine
nop ;set breakpoint here
hexinloop:
bsr nibblein ;convert digit
cmp $10,d0 ;test if good
bcc hexinok ;no,then done
lsl.l #4,d1 ;shift result
or.b d0,d1 ;insert nibble
bra hexinloop ;and continue
hexinok:
rts
nibblein: ;convert the nibble from (A0)
clr.l d0 ;erase D0
move.b (a0)+,d0 ;get digit,increment A0
sub #'A',d0 ;subtract $41
bcc ischar ;no problem:in the range A-F
add #7,d0 ;else correct value
ischar:
add #10,d0 ;correct value
rts
string:DC.B "56789ABC',00 ;8 digit string ending with a
;null byte to be converted
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the NOP,to set the breakpoint in AssemPro select the correct
address with the mouse and press the right-Amiga-B keys.Start the
program and watch the contents of D1,the hex value is placed in
this register.
This is the method for converting hex to binary.If you convert
decimal to binary,the conversion is not much harder.
4.3.4.Converting ASCII To Decimal.
---------------------------------
You can use a very similar method to the one used above.Since you
are not sure how many digits there are,you'll use a similar method
for putting digits of a number in the next place up.You can't do
this with shifting,but you can multiply by 10 and add the value of
the digit.
Heres the program for converting decimal numbers.
decin: ;converting a decimal number
clr.l d1 ;first erase D1
move.l #string,a0 ;the string to convert
jsr decinloop ;test subroutine
nop ;breakpoint here
decinloop:
bsr digitin ;convert digit
cmp #10,d0 ;test,if valid
bcc decinok ;no,then done
mulu #10,d1 ;shift result
add d0,d1 ;insert nibble
bra decinloop ;and continue
decinok:
rts ;end of conversion
digitin: ;converting the nibble from (A0)
clr.l d0 ;erase D0
move.b (a0)+,d0 ;get digit,increment A0
sub #'0',d0 ;subtract $30
rts
string:dc.b '123456' ;ASCII decimal string to convert
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the NOP,to set the breakpoint in AssemPro select the correct
address with the mouse and press thr right-Amiga-B keys.Select
"Parameter-Output-numbers-Decimal"so the registers are displayed
as decimal numbers.Then start the program and watch the contents
of D1,the decimal value is placed in this register.
This program can ONLY convert numbers upto 655350,although the hex
conversion routine can go higher.Thats because the MULU command
can only multiply 16-bit words.The last multiplication that can be
done correctly is $FFFF*10--65535*10,which gives us the value
655350.Normally this is a large enough range,so you won't
complicate the program further.
NOW LOAD PART 2
end.